มาแก้ปัญหา “AFBF+CGHB+DAFG+AEAB=BCBC” กันเถอะ
“In programming, the hard part isn’t solving problems, but deciding what problems to solve.” – Paul Graham
“ในการเขียนโปรแกรมนั้น การแก้ปัญหาไม่ใช่สิ่งที่ยากที่สุด แต่สิ่งที่ยากที่สุดคือการตัดสินว่าปัญหาใดบ้างที่ควรได้รับการแก้ไข” – พอล เกรแฮม
Paul Graham ‘s quotes
หากข้อความข้างต้นนั้นถูกต้อง ผมก็คิดว่าปัญหาของนักเรียนประถมอันโด่งดังอย่าง “AFBF+CGHB+DAFG+AEAB=BCBC” ก็ควรได้รับการแก้ไขอย่างเป็นรูปธรรมเสียที การจะส่งต่อปัญหานี้ไปสู่คนรุ่นถัดไปนั้น ดูจะเป็นการไร้ความรับผิดชอบต่อคนรุ่นหลังอย่างรุนแรง เกินกว่าที่จะยอมรับได้ ดังนั้นเราจึงเริ่มพัฒนาโค้ดขึ้นมา เพื่อให้มันไม่ใช่ปัญหาอีกต่อไป แม้ว่ามันจะสร้างปัญหาอื่นตามมาก็ตาม
จุดเริ่มต้น
ทันทีที่เห็นคำถาม “AFBF+CGHB+DAFG+AEAB=BCBC” ในเว็บบอร์ดพันทิป ก็ทราบได้ทันทีว่านี่คงเป็นปัญหาใหญ่มากทีเดียว ผมจึงเริ่มโดยการวิเคราะห์ว่าจะมีหนทางใดบ้างที่แก้ปัญหานี้ได้ สุดท้ายเพื่อให้ปัญหานี้ได้รับการแก้ไขอย่างแท้จริง ผมจึงเลือกที่จะเขียนโค้ดเพื่อหาคำตอบของสมการแบบนี้ โดยกำหนดคุณลักษณะไว้ดังนี้
- ตัวอักษรทุกตัวจะมีค่าเป็นเลขโดด และไม่ซ้ำกับตัวอักษรตัวอื่น ดังนั้นแล้วจะมีตัวอักษรได้เพียง 10 ตัวเท่านั้นในโจทย์
- รองรับโจทย์ที่มีได้ทั้ง + และ – อยู่ปะปนกัน
- ไม่จำเป็นว่าจำนวนหลักจะต้องเท่ากัน
- รองรับโจทย์ที่มีตัวเลขแทรกอยู่ในโจทย์ได้
- บันทึกผลเป็นไฟล์ได้
วิเคราะห์โจทย์
ต่อมาก็ต้อง วิเคราะห์ว่า ผมควรจะทำอะไรกับโจทย์ที่เป็นข้อความยาวๆนี้บ้าง
สิ่งที่ต้องหาแน่ๆเลยคือ “สัญลักษณ์ – เครื่องหมาย” ต่างๆในโจทย์ เพราะมันจะบอกเราว่าในโจทย์นี้มีกี่กลุ่มข้อความ และแต่ละข้อความต้องทำอะไรบ้าง ส่วนไหนคือผลลัพธ์ ส่วนไหนคือส่วนของโจทย์
หลังจากนั้นเราจึงเข้าไปแยกดูในแต่ละชุดความความ และผลลัพธ์ว่ามีตัวอักษรอะไรบ้าง และรวมกันแล้วมีตัวอักษรรวมทั้งหมดกี่ตัว และในตัวอักษรนั้นมีเป็นตัวเลขกี่ตัว และอักษรกี่ตัว ซึ่งตัวเลขนั้นจะให้ค่าเท่ากับตัวเลขตัวนั้นไว้ และจะหาค่าเฉพาะส่วนที่เป็นตัวอักษรเท่านั้น
สรุปว่า สิ่งที่เราจะได้จากการแตกโจทย์เป็นส่วนๆนั้นคือ
- ชุดข้อความ
- รายการตัวอักษร
- รายการตัวเลข
- ลำดับเครื่องหมาย
- ชุดผลลัพธ์
วิธีการที่จะใช้คือ
- แทนค่าตัวอักษรด้วยค่าตัวเลข 0-9 โดยที่ค่าตัวเลขนั้นจะไม่ซ้ำกันกับค่าในตัวอักษรอื่น และไม่ซ้ำกับตัวเลขที่มีอยู่แล้ว
- แทนค่าตัวอักษร และตัวเลข ในชุดข้อมูลทุกชุด รวมถึงผลลัพธ์
- คำนวนตามลำดับชุดข้อมูล และลำดับเครื่องหมาย
- ตรวจสอบว่าผลลัพธ์จากการคำนวน ตรงกับค่าที่ได้จากการแทนค่าในชุดข้อมูลผลลัพธ์หรือไม่
- ถ้าได้ผลลัพธ์ตรงกัน เก็บค่านั้นใว้ และนำมาแสดงในรายงาน
- โปรแกรมจะวนจนครบทุกค่าที่เป็นไปตามเงื่อนไขข้อที่ 1.
ลงมือเขียนสักที
ฟังค์ชั่น main
1 2 | print("Welcome to Find for What?") input_equation = input("What's your Equation : ") |
สองบันทัดนี้เขียนใว้สำหรับรับค่าผ่าน Terminal ไปเก็บเป็นข้อความใว้ในตัวตัวแปร input_equation
เพื่อนำไปตรวจสอบในขั้นตอนต่อไปว่ามีข้อมูลครบหรือไม่
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | for c in input_equation: if c is '=': eqaulSym_count += 1 elif c in ['+' , '-']: operandSym_count += 1 elif c.isalnum(): alnumSym_count += 1 dict_char[c] = 0 if c not in dict_char_count.keys(): dict_char_count[c] = 1 else : dict_char_count[c] += 1 else: notAllow_count += 1 notAllow_char += c |
โค้ดในส่วนนี้ ใช้การตรวจสอบข้อความที่รับมาว่ามีครบตามสิ่งที่ต้องการหรือไม่
และตรงตามเงื่อนไขหรือไม่ ซึ่งข้อความนั้นจะสามารถมีได้เพียงตัวอักษร A-Z ตัวเลข 0-9 และเครื่องหมาย + , – , = เท่านั้น และไม่รองรับการเว้นวรรค (spacebar) ด้วย
ถ้าหากเป็นตัวอักษรหรือตัวเลข จะถูกนำไปเก็บในรูปแบบตัวแปรแบบ Dictionary
ที่ชื่อ dict_char
โดยตัวอักษร หรือตัวเลขนั้น จะถูกใช้เป็น คีย์ (key) ของตัวแปร Dictionary โดยมีค่า (value) เริ่มต้นของแต่ละคีย์ เป็น 0
ตัวแปร eqaulSym_count
ใช้เพื่อนับจำนวนเครื่องหมาย ‘=’ ที่มีในโจทย์ ซึ่งแต่ละโจทย์จะมีได้เพียง 1 ตัวเท่านั้น
ตัวแปร operandSym_count
ใช้เพื่อนับจำนวนเครื่องหมาย ‘+’ และ ‘-‘ ในโจทย์ ซึ่งแต่ละโจทย์ จะต้องมีเครื่องหมายอย่างน้อย 1 ชนิด
ตัวแปร notAllow_count
ใช้เพื่อนับจำนวนอักษรที่ไม่รองรับในข้อความ
จากในส่วนนี้ จะทำให้เราได้ชุดตัวแปร Dictionary ที่มีชนิดของตัวอักษร และตัวเลขทุกตัว ของโจทย์ อยู่ในตัวแปรที่ชื่อ dict_char
1 2 3 4 5 6 7 8 9 10 11 12 | if not (eqaulSym_count == 1): raise IOError("your Equation have no \"=\"") elif operandSym_count == 0: raise IOError("your Equation have no operator [+ or -]") elif notAllow_count > 0: raise IOError("your Equation have unsupport charactor : {}".format(notAllow_char)) elif len(input_equation) == (eqaulSym_count+operandSym_count+alnumSym_count): print("Status : Check passed") print("your Equation : {}".format(input_equation)) eqmain , res = input_equation.split('=') print("Equation : {} and Result : {}".format(eqmain,res)) |
ในโค้ดส่วนนี้คือการตรวจสอบว่า ค่าที่ได้จากโค้ดในส่วนก่อนหน้านั้น มีค่าใดบ้างที่แสดงให้เห็นถึงความผิดของข้อความที่รับมา เช่นจำนวนเครื่องหมาย ‘=’ ที่อาจจะมีมากกว่าหนึ่งแห่ง หรือมีสัญลักษณ์อื่นๆที่ระบบไม่รองรับ อยู่ในข้อความ
โค้ดส่วนนี้ก็จะไปทำให้ exception ในส่วน IOError ที่อยู่ท้ายฟังค์ชั่นทำงาน และหลุดจากโปรแกรม
แต่ถ้าหากทุกๆอย่างปกติ ระบบก็จะแสดงข้อความ Status : Check passed
และแสดง โจทย์ที่เราใส่เข้าไปให้ดูอีกครั้ง
ในโค้ดถัดมา ระบบจะแยกข้อความออกเป็นสองส่วน โดยเป็นส่วนของคำถาม ที่อยู่ก่อนเครื่องหมาย ‘=’ และส่วนที่อยู่หลังเครื่องหมาย ‘=’ จะเป็นส่วนของคำตอบ
ซึ่งระบบจะแสดงทั้งสองส่วนให้เราเห็นด้วย
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | if '+' not in eqmain: eq = eqmain.split('-') for eqsub in eq: lsoperand.append('-') lseq.append(eqsub) lsoperand.pop(len(lsoperand)-1) elif '-' not in eqmain: eq = eqmain.split('+') for eqsub in eq: lsoperand.append('+') lseq.append(eqsub) lsoperand.pop(len(lsoperand)-1) else: eq = eqmain.split('+') for eqsub in eq: # print("eqsub : {}".format(eqsub)) if '-' in eqsub: lsoperand.append('+') lseqsub = eqsub.split('-') for eqsubsub in lseqsub: # print("eqsubsub : {}".format(eqsubsub)) lsoperand.append('-') lseq.append(eqsubsub) lsoperand.pop(len(lsoperand)-1) # print("lsoperand : {}".format(lsoperand)) else: lsoperand.append('+') lseq.append(eqsub) lsoperand.pop(0) |
โค้ดในส่วนนี้จะเป็นส่วนที่ใช้ในการแบ่งส่วนของคำถาม ออกเป็นข้อความ โดยแบ่งจากเครื่องหมาย ‘+’ และ ‘-‘ ที่มีอยู่ในส่วนของคำถามนั้น และเก็บข้อความนั้นลงในตัวแปลชนิด List ที่ชื่อ lseq
รวมถึงเก็บเครื่องหมายในคำถามลงในตัวแปรชนิด List อีกตัวที่ชื่อ lsoperand
ซึ่งตัวแปรทั้งสองตัวจะถูกเก็บตามลำดับตำแหน่งที่อยู่ในชุดคำถาม
เช่น A+B-C ในตัวแปร lseq
จะมีค่าดังนี้ [‘A’ , ‘B’ , ‘C’] และในตัวแปร lsoperand
จะมีค่าดังนี้ [‘+’ , ‘-‘]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | len_val = len(dict_char.keys()) offset_val = 0 s_number = "" lsNumberKey = [] lsAlphaKey = [] for c in dict_char.keys(): if c.isdigit(): # print("{} is number".format(c)) len_val -= 1 s_number += c lsNumberKey.append(c) else: lsAlphaKey.append(c) print("dict key length : {}".format(len_val)) maxofnum = int(10**len_val) minofnum = int(startnum[:len_val]) if s_number: offset_val = int(s_number)*maxofnum len_val = len(dict_char.keys()) print("from : {} run to : {}".format(minofnum+offset_val,(maxofnum+offset_val)-1)) print("list of group : {} - list operand : {} - Result : {}".format(lseq,lsoperand,res)) print("list of Alpha : {} - list of Number : {}".format(lsAlphaKey,lsNumberKey)) print("dict of char : {}".format(dict_char)) print("frequency using : {}".format(dict_char_count)) startTime = datetime.datetime.now() print("Start Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) |
ตัวแปร len_val
คือตัวแปรที่ใช้เก็บจำนวนตัวอักษรทุกตัวรวมถึงตัวเลขที่มีอยู่ในโจทย์ของเราด้วย จากนั้นในส่วนถัดมาจะเป็นการแยกระหว่างตัวอักษรและตัวเลข ออกจากกัน
และมีการเก็บค่าตัวเลขไว้ใน s_number
โดยเรียงตามลำดับ และถูกเก็บแยกแต่ละตัวในตัวแปรชนิด List ในตัวแปร lsNumberKey
โดยเรียงตามลำดับและเก็บตัวอักษรในตัวแปรชนิด List ไว้ใน lsAlphaKey
และเรียงตามลำดับเช่นกันmaxofnum
เป็นตัวแปรที่เก็บค่าที่มากที่สุดที่จะนับไปถึง เช่นถ้ามีตัวอักษรเพียงตัวเดียว ระบบก็จะนับสูงสุดคือ 10 หรือถ้าหากเป็น 2 ตัวอักษรก็จะนับไปถึง 100minofnum
เป็นตัวแปรที่ใช้เก็บค่าเริ่มต้นในการนับ เช่นถ้าหากมีอักษรเพียงตัวเดียวก็จะเริ่มที่ 0 แต่ถ้าหากมี 2 ตัวก็จะเริ่มที่ 01 และถ้าหากมี 3 ตัวอักษรก็จะเริ่มที่ 012offset_val
ใช้เก็บค่า Offset ที่เกิดจากการมีตัวเลขในชุดข้อมูล เพื่อให้ระบบไม่นับซ้ำในส่วนของตัวเลข ซึ่งจะทำให้ลดจำนวนตัวเลขที่จะต้องนับได้มากทีเดียว
เช่น ถ้าหากโจทย์คือ “A1+A3=BB” maxofnum
จากการมีตัวอีกษร 2 ตัว คือ 100minofnum
จากการที่มี 2 ตัวอักษร คือ 01offset_val
จากตัวเลข 2 ตัวคือ 1 และ 3 จะเท่ากับ 1300
ดังนั้นระบบจะเริ่มนับจาก minofnum+offset_val
เท่ากับ 1301 ไปจนถึง maxofnum+offset_val
เท่ากับ 1399 นั่นคือนับแค่ 99 ค่า สำหรับอักษร 2 ตัว
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | for cnt in range(minofnum,maxofnum): str_cnt = "{:010d}".format(cnt) # print("stringofnum : {}".format(str_cnt)) str_cnt = str_cnt[len(str_cnt)-len(lsAlphaKey):] goodNumber = True dict_letter = {} # print("stringofnum : {}".format(str_cnt)) for letter in str_cnt: if letter in lsNumberKey: # print("False Letter : {}".format(letter)) goodNumber = False break elif letter not in dict_letter.keys(): dict_letter[letter] = 1 # print("good Letter : {}".format(dict_letter)) else: # print("False Letter : {}".format(letter)) goodNumber = False break dict_pack = {} if goodNumber: # print("stringofnum : {}".format(str_cnt)) c_count = 0 for c in lsAlphaKey: # print("{} is not number : {}".format(c , str_cnt[c_count])) dict_char[c] = int(str_cnt[c_count]) c_count += 1 for n in lsNumberKey: # print("{} is number".format(n)) dict_char[n] = int(n) # print("dict : {}".format(dict_char)) dict_pack['dict_char'] = dict(dict_char) dict_pack['ls_Equat'] = lseq dict_pack['ls_Operand'] = lsoperand dict_pack['ls_Result'] = res lsNumber.append(dict_pack) |
เมื่อได้ค่าเริ่มต้นและค่าสุดท้ายที่จะวนทดสอบเพื่อหาคำตอบกันแล้ว แต่ค่าที่อยู่ในระหว่างค่าเริ่มต้นกับค่าสุดท้ายนั้น มีค่าที่มีตัวเลขซ้ำกันอยู่มากมาย ดังนั้นเราต้องมากรองเอาค่าพวกนั้นออกไปก่อน วิธีคือเราจะวนในทุกๆค่าที่อยู่ระหว่างค่าเริ่มต้น ไปจนถึงค่าสุดท้าย
แล้วตรวจดูทีละค่าว่ามีตัวเลขซ้ำกันหรือไม่ โดยการแปลงค่าที่กำลังนับอยู่นั้นให้อยู่ในรูปของข้อความ แล้ววนเทียบตัวอักษรทีละตัว ถ้าหากมีเลขที่ซ้ำกันก็จะข้ามจำนวนนั้นไป
แต่ถ้าหากจำนวนนั้นไม่มีเลขที่ซ้ำกันเลย จำนวนนั้นก็จะถูกแยกกลับเข้าไปเก็บในตัวแปร dict_char
โดยเรียงตามลำดับ ก่อนที่จะแพ็ค dict_char
, ชุดข้อความ lseq
, ชุดเครื่องหมาย lsoperand
และผลลัพธ์ res
รวมกันในตัวแปรชนิด Dictionary ที่ชื่อว่า dict_pack
ที่เลือกใช้ตัวแปรชนิด Dictionary ก็เพราะว่ามันชัดเจนเวลาอ้างอิงตอนใช้งาน แล้วก็เพิ่มค่า dict_pack
ลงในตัวแปรชนิด List ที่ชื่อ lsNumber
ทำให้ใน lsNumber
มีค่าทุกค่าที่ผ่านเงื่อนไขมาครบแล้ว และพร้อมจะนำไปทดสอบว่ามีค่าใดบ้างที่เมื่อแทนค่าในโจทย์แล้ว ได้คำตอบที่ถูกต้อง
1 2 | if lsNumber: proc = pl.map(findValue , lsNumber) |
ด้วยความที่เราจะต้องทดสอบในทุกๆ ค่าที่มีใน lsNumber
ซึ่งจะมีจำนวนสมาชิกขึ้นกับจำนวนตัวอักษรในโจทย์ที่เราใส่เข้าไป ซึ่งเป็นไปได้ว่าอาจจะมีจำนวนมากถึงหลักล้านค่า หรือหลายล้านค่า ซึ่งถ้าหากปล่อยให้โปรแกรมทำงานในรูปแบบ Single Processing ตามปกติ คงจะใช้เวลานานมากๆกว่าจะทำงานได้ครบทั้งหมด ผมจึงเลือกใช้วิธี Multiprocessing โดยใช้ pool of workers ในการรันโปรแกรมนี้
และสร้างฟังค์ชั่น findValue
ไว้สำหรับทดสอบค่าที่รับจากตัวแปรประเภท Dictionary ที่กำหนดเข้ามา และส่งค่ากลับเป็นตัวแปรชนิด Dictionary เช่นกัน
ซึ่งภายในตัวแปร Dictionary นั้นประกอบไปด้วย คีย์ ‘Result’ ที่บ่งบอกผลลัพธ์ว่าค่าที่ใส่เข้ามานั้นถูกต้องหรือไม่ เป็นแบบ Boolean มีแค่ True หรือ False เท่านั้น
และคีย์ ‘Value’ ที่เป็นค่าเดียวกับที่รับค่าเข้ามาในฟังค์ชั่นนี้
ค่าที่ได้จากฟังค์ชั่น findValue
นี้จะถูกเก็บไว้ในตัวแปร List ที่ชื่อ proc
1 2 | cpu_count = multiprocessing.cpu_count() pl = multiprocessing.Pool(processes = int(cpu_count)) |
cpu_count
คือตัวแปรที่ใช้เก็บจำนวน Processor Core ที่มีในเครื่องpl
คือ pool ของ Worker ที่มีจำนวนเท่ากับที่กำหนดผ่าน processes = int(cpu_count)
ซึ่งสามารถกำหนดเป็นค่าอื่นๆก็ได้เช่นกัน โดยไม่ต้องอ้างอิงกับจำนวน Processor Core ในเครื่องที่ใช้ เช่นอาจจะกำหนดให้ Processes = 8 หรือ Processes = int(cpu_count)*2 ก้ได้เช่นกัน แต่ที่ผมกำหนดให้เท่ากันนั้นเพราะว่าไม่ต้องการให้โปรแกรมนี้ดึงทรัพยากรของเครื่องมากเกินไป ซึ่งทรัพยากรเครื่องก็มีเหลืออยู่ไม่มาก หลังจากที่หมดไปกับ Chrome , Spotify และตัว VSCode แล้วนี่แหละ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | flists = open("Resultlists.txt", 'w') flists.write("Input Equation : {}\n".format(input_equation)) for dict_result in proc: # print("Result : {} - Value : {}".format(dict_result['Result'] , dict_result['Value'])) if dict_result['Result'] == True: value = dict_result['Value'] flists.write("Correct Value : {}\n".format(value)) print("Correct Value : {}".format(value)) s = "" for c in input_equation: if c in value.keys(): s += str(value[c]) else: s += c flists.write("Equation Value : {}\n".format(s)) print("Equation Value : {}".format(s)) stopTime = datetime.datetime.now() diffTime = stopTime-startTime print("Start Time : {} - Stop Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z") , \ stopTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) print("Use Time : {}".format(diffTime)) flists.write("Start Time : {} - Stop Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z") , \ stopTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) flists.write("Use Time : {}".format(diffTime)) print("lsNumber Count : {} Proc Count : {}".format(len(lsNumber) , len(proc))) flists.write("Run Count : {}\n".format(len(lsNumber))) flists.close() |
หลังจากที่ได้ค่าจากฟังค์ชั่น findValue
ที่ถูกเก็บใว้ในตัวแปร proc
ครบแล้ว
ขั้นต่อมาคือการคัดเอาเฉพาะค่าที่ถูกต้องเก็บไว้ในไฟล์ที่ชื่อ Resultlists.txt
ซึ่งโปรแกรมจะแสดงค่าที่ถูกต้อง พร้อมทั้งแทนค่าในโจทย์ เพื่อให้ตรวจสอบได้ง่ายขึ้นว่าสิ่งที่ได้ ถูกต้องจริงหรือมีข้อผิดพลาดใดหรือไม่ พร้อมทั้งบันทึกค่า ทั้งค่าที่ถูก และโจทย์ที่ถูกแทนค่าแล้วลงในไฟล์ Resultlists.txt
ด้วย
นอกจากนั้นแล้วยังแสดง เวลาที่เริ่มต้นทำงาน ไปจนถึงเวลาที่โปรแกรมทำงานแล้วเสร็จ
และจำนวนที่ระบบ พร้อมทั้งระยะเวลา ที่ได้รันไปเพื่อหาคำตอบของโจทย์ข้อนี้อีกด้วย
ฟังค์ชั่น findValue
1 2 3 4 | dict_val = Dict_Pack['dict_char'] lsequat = Dict_Pack['ls_Equat'] lsoperands = Dict_Pack['ls_Operand'] res = Dict_Pack['ls_Result'] |
ฟังค์ชั่นนี้เริ่มต้นด้วยเก็บค่าตัวแปรประเภท Dictionary ที่รับเข้ามาในชื่อ Dict_Pack
และแยกค่าในแต่ละคีย์ใว้ในตัวแปรแต่ละตัว โดยdict_val
ใช้เก็บค่าของตัวอักษรแต่ละตัวซึ่งอยู่ในคีย์ 'dict_char'
lsequat
ใช้เก็บค่าชุดข้อความเรียงตามลำดับ ซึ่งถูกเก็บใว้ในคีย์ 'ls_Equat'
lsoperands
ใช้เก็บค่าเครื่องหมายเรียงตามลำดับ ซึ่งถูกเก็บใว้ในคีย์ 'ls_Operand'
res
ใช้เก็บผลลัพธ์ของโจทย์ ซึ่งเก็บไว้ในคีย์ 'ls_Result'
dict_val
ใช้เก็บค่าของตัวอักษรแต่ละตัวซึ่งอยู่ในคีย์ 'dict_char'
lsequat
ใช้เก็บค่าชุดข้อความเรียงตามลำดับ ซึ่งถูกเก็บใว้ในคีย์ 'ls_Equat'
lsoperands
ใช้เก็บค่าเครื่องหมายเรียงตามลำดับ ซึ่งถูกเก็บใว้ในคีย์ 'ls_Operand'
res
ใช้เก็บผลลัพธ์ของโจทย์ ซึ่งเก็บไว้ในคีย์ 'ls_Result'
1 2 3 4 5 6 7 8 9 10 11 12 | for alphaVal in lsequat: s="" for c in alphaVal: s += str(dict_val[c]) # print("str : {}".format(s)) lsValInt.append(int(s)) # print("list Value : {}".format(lsValInt)) s="" for c in res: s += str(dict_val[c]) # print("str : {}".format(s)) lsValInt.append(int(s)) |
ส่วนนี้คือส่วนที่แทนค่าของแต่ละตัวอักษรจาก dict_val
ลงในชุดข้อความ lsequat
และ res
และเก็บค่าที่ได้จากการแทนค่าแล้วในรูปแบบเลขจำนวนเต็มลงในตัวแปรประเภท List ที่ชื่อ lsValInt
1 2 3 4 5 6 7 8 9 10 | temp = lsValInt[0] count = 0 for oper in lsoperands: count += 1 if oper is '+': temp += lsValInt[count] elif oper is '-' : temp -= lsValInt[count] else: temp += 0 |
จากนั้นก็เอาค่าที่อยู่ใน lsValInt
มาคำนวนตามลำดับเครื่องหมายที่เก็บใว้ในตัวแปร lsoperands
โดยจะได้ผลลัพธ์อยู่ในตัวแปร temp
ซึ่งการคำนวนนี้ ค่าสุดท้ายใน lsValInt
จะไม่ถูกนำมาคำนวนร่วม เพราะเป็นค่าของผลลัพธ์ แต่บางท่านอาจจะเลือกเอาค่านี้มาลบจาก temp
เพื่อดูว่าถ้าได้ค่า ที่เหลือเป็น 0 ก็แปลว่า ค่าที่อยู่ในแต่ละตัวอักษรนั้นทำให้โจทย์เป็นจริงได้เช่นกัน
เพียงแต่เราเลือกที่จะเอาไปตรวจสอบในส่วนสุดท้ายแทน ซึ่งไม่ได้มีผลลัพธ์ที่แตกต่างกันอย่างไร
1 2 3 4 5 6 7 8 9 10 | if not temp == lsValInt[-1]: # print("result not correct") print("Value : {}".format(dict_res['Value'])) res_notcorrect = True dict_res['Result'] = False else : # print("result correct") print("Value : {} - {:>15}".format(dict_res['Value'], "Correct")) res_notcorrect = False dict_res['Result'] = True |
ส่วนสุดท้ายของฟังก์ชันนี้คือการตรวจสอบว่า ค่าที่ได้จากการแทนค่าในชุดข้อความ และคำนวนตามชุดเครื่องหมายนั้น ได้ค่าเท่ากับค่าที่ได้จากการแทนค่าในส่วนของผลลัพธ์หรือไม่
ซึ่งในส่วนนี้จะมีการแสดงค่าที่รับเข้ามา และผลลัพธ์ว่าใช้ได้หรือไม่ด้วย
ซึ่งค่าเหล่านั้นถูกเก็บใว้ในตัวแปรประเภท Dictionary ที่ชื่อ dict_res
โดยผลของการตรวจสอบจะเก็บเป็น Boolean ในคีย์ 'Result'
และค่าที่รับเข้ามาจะเก็บอยู่ในคีย์ 'Value'
dict_res
นี่คือค่าที่จะได้จากการทำงานของฟังค์ชั่นนี้
มาลองใช้งานกัน
เริ่มต้นด้วยการรันไฟล์ main.py ใน Terminal
1 | >python -m main.py</code> #สำหรับ CMD บน Windows |
1 | $python main.py</code> #สำหรับ Terminal บน Linux |
บน Console ก็จะมีข้อความ ขึ้นมาว่า “Welcome to Find for What?”
และ “What’s your Equation : ” และมี cursor ให้เราใส่โจทย์ลงไป
โดยโจทย์จะประกอบด้วยตัวอักษร A-Z , 0-9 เครื่องหมาย ‘+’ , ‘-‘ และ ‘=’
เครื่องหมาย ‘=’ จะมีได้เพียง 1 อันใน 1 โจทย์เท่านั้น
ทีนี้เราจะลองใส่โจทย์เจ้าปัญหาของเรานั่นคือ AFBF+CGHB+DAFG+AEAB=BCBC ลงไปเพื่อหาคำตอบกันนะครับ
และกดปุ่ม “Enter” เพิ่มเริ่มการคำนวน
โปรแกรมจะแสดงข้อมูลต่างๆที่เราได้ใส่เข้าไป ทั้งจำนวนตัวอักษร จำนวนตัวเลขที่จะวนทำงานจากนั้นโปรแกรมก็จะเริ่มทำงาน
และเราก็เพียงรอ…. และภาวนา เท่านั้นเอง จนกว่าโปรแกรมจะทำงานจบ
เมื่อโปรแกรมทำงานจนจบแล้ว
โปรแกรมจะแสดงค่าที่ถูกต้อง ซึ่งมีเพียง 4 ค่าเท่านั้น ที่แทนแล้วจะทำให้โจทย์นั้นเป็นจริงได้ หลังจากผ่านคำนวนจำนวนด้วยค่าที่ไม่ซ้ำกัน 1,814,400 ค่า และใช้เวลาไป 3 ชั่วโมง 12 นาที กับอีก 15 วินาที นับว่านานทีเดียว
และเราสามารถดูข้อมูลคำตอบได้ในไฟล์ Resultlists.txt ได้เช่นกัน เพื่อความสะดวกในกรณีที่คำตอบมีปริมาณเยอะๆ
โค้ดทั้งหมด
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | import sys from random import randint import multiprocessing from multiprocessing import Manager as manager import datetime def findValue(Dict_Pack = {}): lsValInt = [] dict_val = Dict_Pack['dict_char'] lsequat = Dict_Pack['ls_Equat'] lsoperands = Dict_Pack['ls_Operand'] res = Dict_Pack['ls_Result'] dict_res = {} try: # print("Dict_Pack = {}".format(Dict_Pack)) for alphaVal in lsequat: s="" for c in alphaVal: s += str(dict_val[c]) # print("str : {}".format(s)) lsValInt.append(int(s)) # print("list Value : {}".format(lsValInt)) s="" for c in res: s += str(dict_val[c]) # print("str : {}".format(s)) lsValInt.append(int(s)) temp = lsValInt[0] count = 0 for oper in lsoperands: count += 1 if oper is '+': temp += lsValInt[count] elif oper is '-' : temp -= lsValInt[count] else: temp += 0 # print("out of operand") # print("temp {} : {}".format(count,temp)) # print("temp : {} = res : {}".format(temp,lsValInt[-1])) dict_res['Value'] = dict_val if not temp == lsValInt[-1]: # print("result not correct") print("Value : {}".format(dict_res['Value'])) res_notcorrect = True dict_res['Result'] = False else : # print("result correct") print("Value : {} - {:>15}".format(dict_res['Value'], "Correct")) res_notcorrect = False dict_res['Result'] = True except IOError as ioerror: print("Error on {}".format(ioerror)) else: return dict_res def main(): eqaulSym_count = 0 operandSym_count = 0 alnumSym_count = 0 notAllow_count = 0 notAllow_char = "" proc = [] lseqsub = [] lseq = [] lsoperand = [] lsVal = [] lsNumber = [] res_notcorrect = True lsrand = [] dict_char = {} dict_pack = {} dict_char_count = {} startnum = "0123456789" cpu_count = multiprocessing.cpu_count() pl = multiprocessing.Pool(processes = int(cpu_count)) try: print("Welcome to Find for What?") input_equation = input("What's your Equation : ") for c in input_equation: if c is '=': eqaulSym_count += 1 elif c in ['+' , '-']: operandSym_count += 1 elif c.isalnum(): alnumSym_count += 1 dict_char[c] = 0 if c not in dict_char_count.keys(): dict_char_count[c] = 1 else : dict_char_count[c] += 1 else: notAllow_count += 1 notAllow_char += c if not (eqaulSym_count == 1): raise IOError("your Equation have no \"=\"") elif operandSym_count == 0: raise IOError("your Equation have no operator [+ or -]") elif notAllow_count > 0: raise IOError("your Equation have unsupport charactor : {}".format(notAllow_char)) elif len(input_equation) == (eqaulSym_count+operandSym_count+alnumSym_count): print("Status : Check passed") print("your Equation : {}".format(input_equation)) eqmain , res = input_equation.split('=') print("Equation : {} and Result : {}".format(eqmain,res)) if '+' not in eqmain: eq = eqmain.split('-') for eqsub in eq: lsoperand.append('-') lseq.append(eqsub) lsoperand.pop(len(lsoperand)-1) elif '-' not in eqmain: eq = eqmain.split('+') for eqsub in eq: lsoperand.append('+') lseq.append(eqsub) lsoperand.pop(len(lsoperand)-1) else: eq = eqmain.split('+') for eqsub in eq: # print("eqsub : {}".format(eqsub)) if '-' in eqsub: lsoperand.append('+') lseqsub = eqsub.split('-') for eqsubsub in lseqsub: # print("eqsubsub : {}".format(eqsubsub)) lsoperand.append('-') lseq.append(eqsubsub) lsoperand.pop(len(lsoperand)-1) # print("lsoperand : {}".format(lsoperand)) else: lsoperand.append('+') lseq.append(eqsub) lsoperand.pop(0) len_val = len(dict_char.keys()) offset_val = 0 s_number = "" lsNumberKey = [] lsAlphaKey = [] for c in dict_char.keys(): if c.isdigit(): # print("{} is number".format(c)) len_val -= 1 s_number += c lsNumberKey.append(c) else: lsAlphaKey.append(c) print("dict key length : {}".format(len_val)) maxofnum = int(10**len_val) minofnum = int(startnum[:len_val]) if s_number: offset_val = int(s_number)*maxofnum len_val = len(dict_char.keys()) print("from : {} run to : {}".format(minofnum+offset_val,(maxofnum+offset_val)-1)) print("list of group : {} - list operand : {} - Result : {}".format(lseq,lsoperand,res)) print("list of Alpha : {} - list of Number : {}".format(lsAlphaKey,lsNumberKey)) print("dict of char : {}".format(dict_char)) print("frequency using : {}".format(dict_char_count)) startTime = datetime.datetime.now() print("Start Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) # flists = open("Numberlists.txt", 'w') for cnt in range(minofnum,maxofnum): str_cnt = "{:010d}".format(cnt) # print("stringofnum : {}".format(str_cnt)) str_cnt = str_cnt[len(str_cnt)-len(lsAlphaKey):] goodNumber = True dict_letter = {} # print("stringofnum : {}".format(str_cnt)) for letter in str_cnt: if letter in lsNumberKey: # print("False Letter : {}".format(letter)) goodNumber = False break elif letter not in dict_letter.keys(): dict_letter[letter] = 1 # print("good Letter : {}".format(dict_letter)) else: # print("False Letter : {}".format(letter)) goodNumber = False break dict_pack = {} if goodNumber: # print("stringofnum : {}".format(str_cnt)) c_count = 0 for c in lsAlphaKey: # print("{} is not number : {}".format(c , str_cnt[c_count])) dict_char[c] = int(str_cnt[c_count]) c_count += 1 for n in lsNumberKey: # print("{} is number".format(n)) dict_char[n] = int(n) # print("dict : {}".format(dict_char)) dict_pack['dict_char'] = dict(dict_char) dict_pack['ls_Equat'] = lseq dict_pack['ls_Operand'] = lsoperand dict_pack['ls_Result'] = res lsNumber.append(dict_pack) # flists.write("Count : {} - Dict Pack Value : {}\n".format(str_cnt , dict_pack)) # print("list of dict char : {}".format(lsNumber)) # flists.close() if lsNumber: proc = pl.map(findValue , lsNumber) # print("Proc : {}".format(proc)) flists = open("Resultlists.txt", 'w') flists.write("Input Equation : {}\n".format(input_equation)) for dict_result in proc: # print("Result : {} - Value : {}".format(dict_result['Result'] , dict_result['Value'])) if dict_result['Result'] == True: value = dict_result['Value'] flists.write("Correct Value : {}\n".format(value)) print("Correct Value : {}".format(value)) s = "" for c in input_equation: if c in value.keys(): s += str(value[c]) else: s += c flists.write("Equation Value : {}\n".format(s)) print("Equation Value : {}".format(s)) stopTime = datetime.datetime.now() diffTime = stopTime-startTime print("Start Time : {} - Stop Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z") , \ stopTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) print("Use Time : {}".format(diffTime)) flists.write("Start Time : {} - Stop Time : {}".format(startTime.strftime("%Y-%m-%d %H:%M:%S%Z") , \ stopTime.strftime("%Y-%m-%d %H:%M:%S%Z"))) flists.write("Use Time : {}".format(diffTime)) print("lsNumber Count : {} Proc Count : {}".format(len(lsNumber) , len(proc))) flists.write("Run Count : {}\n".format(len(lsNumber))) flists.close() if not lseq or not lsoperand: raise IOError("wrong operation on create list of operate") except IOError as ioerror: print("Error on {}".format(ioerror)) if __name__ == "__main__": main() |