पायथन में सबसे छोटा सुडोकू सॉल्वर - यह कैसे काम करता है?


81

मैं अपने खुद के सुडोकू सॉल्वर के साथ खेल रहा था और जब मैं इस पार आया तो कुछ अच्छे और तेज़ डिज़ाइन के लिए कुछ संकेत तलाश रहा था:

def r(a):i=a.find('0');~i or exit(a);[m
in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3)or a[j]for
j in range(81)]or r(a[:i]+m+a[i+1:])for m in'%d'%5**18]
from sys import*;r(argv[1])

मेरा स्वयं का कार्यान्वयन सुदोक को उसी तरह हल करता है जिस तरह से मैं उन्हें अपने सिर में हल करता हूं, लेकिन यह गुप्त एल्गोरिथ्म कैसे काम करता है?

http://scottkirkwood.blogspot.com/2006/07/shortest-sudoku-solver-in-pygon.html


21
यह ऑबफैक्टेड पर्ल प्रतियोगिता के लिए प्रवेश की तरह दिखता है! मुझे लगा कि अजगर के अंकों में से एक स्वच्छ कोड लिखना है जिसे आसानी से समझा जा सकता है :)
वॉरेन

1
वह अजगर ठीक से अपने इंडेंट की तरह नहीं दिखता है। : /
जेक

18
यह अभी तक एक और सबूत है कि आप किसी भी भाषा में समझ से बाहर कोड लिख सकते हैं।
जेसपेरे

मुझे लगता है कि यह एक कोड गोल्फ उत्तर रहा होगा।
लोरेन Pechtel

2
BTW मुझे पूरा यकीन है कि यह एक प्रतियोगिता के लिए सबसे कम संभव सुडोकू सॉल्वर लिखने के लिए था।
जॉन

जवाबों:


220

ठीक है, आप वाक्य रचना को ठीक करके चीजों को थोड़ा आसान बना सकते हैं:

def r(a):
  i = a.find('0')
  ~i or exit(a)
  [m in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3)or a[j]for j in range(81)] or r(a[:i]+m+a[i+1:])for m in'%d'%5**18]
from sys import *
r(argv[1])

थोड़ी सफाई:

from sys import exit, argv
def r(a):
  i = a.find('0')
  if i == -1:
    exit(a)
  for m in '%d' % 5**18:
    m in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3) or a[j] for j in range(81)] or r(a[:i]+m+a[i+1:])

r(argv[1])

ठीक है, इसलिए यह स्क्रिप्ट एक कमांड-लाइन तर्क की अपेक्षा करता है, और इस पर फ़ंक्शन आर को कॉल करता है। अगर उस तार में कोई शून्य नहीं है, तो आर बाहर निकलता है और अपने तर्क को छापता है।

(यदि किसी अन्य प्रकार की वस्तु को पारित किया जाता है, तो कोई भी शून्य पास करने के बराबर नहीं है, और किसी भी अन्य वस्तु को sys.stderr पर प्रिंट किया जाता है और 1 के बाहर निकलने के कोड में परिणाम होता है। विशेष रूप से, sys.exit ("कुछ त्रुटि संदेश") एक प्रकार है किसी प्रोग्राम में त्रुटि होने पर बाहर निकलने का त्वरित तरीका। http://www.python.org/doc/2.5.2/lib/module-sys.html देखें )

मुझे लगता है कि इसका मतलब यह है कि शून्य खुली जगहों के अनुरूप है, और बिना शून्य वाली पहेली हल हो जाती है। फिर वहाँ है कि बुरा पुनरावर्ती अभिव्यक्ति।

लूप दिलचस्प है: for m in'%d'%5**18

5 ** 18 क्यों? यह पता चला है कि '%d'%5**18मूल्यांकन करता है '3814697265625'। यह एक स्ट्रिंग है जिसमें प्रत्येक अंक 1-9 कम से कम एक बार होता है, इसलिए शायद यह उनमें से प्रत्येक को रखने की कोशिश कर रहा है। वास्तव में, ऐसा दिखता है कि यह क्या r(a[:i]+m+a[i+1:])कर रहा है: पुनरावर्ती कॉलिंग आर, उस स्ट्रिंग से एक अंक द्वारा भरे गए पहले रिक्त के साथ। लेकिन यह केवल तभी होता है जब पहले की अभिव्यक्ति झूठी हो। आइए देखें:

m in [(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3) or a[j] for j in range(81)]

इसलिए प्लेसमेंट केवल तभी किया जाता है जब m उस राक्षस सूची में नहीं है। प्रत्येक तत्व या तो एक संख्या है (यदि पहली अभिव्यक्ति nonzero है) या एक चरित्र (यदि पहली अभिव्यक्ति शून्य है)। मी को एक संभावित प्रतिस्थापन के रूप में खारिज किया जाता है यदि यह एक चरित्र के रूप में प्रकट होता है, जो केवल तभी हो सकता है जब पहली अभिव्यक्ति शून्य हो। अभिव्यक्ति शून्य कब है?

इसके तीन भाग हैं जिन्हें गुणा किया जाता है:

  • (i-j)%9 जो शून्य है यदि i और j 9 का एक जोड़ है, अर्थात एक ही कॉलम।
  • (i/9^j/9) जो शून्य है यदि i / 9 == j / 9, अर्थात एक ही पंक्ति।
  • (i/27^j/27|i%9/3^j%9/3) जो शून्य है यदि ये दोनों शून्य हैं:
    • i/27^j^27 जो शून्य है यदि i / 27 == j / 27, अर्थात तीन पंक्तियों का एक ही ब्लॉक
    • i%9/3^j%9/3 जो शून्य है यदि i% 9/3 == j% 9/3, अर्थात तीन कॉलम का एक ही ब्लॉक

यदि इन तीन भागों में से कोई भी शून्य है, तो संपूर्ण अभिव्यक्ति शून्य है। दूसरे शब्दों में, यदि मैं और j एक पंक्ति, स्तंभ, या 3x3 ब्लॉक साझा करते हैं, तो j का मान i पर रिक्त के लिए उम्मीदवार के रूप में उपयोग नहीं किया जा सकता है। अहा!

from sys import exit, argv
def r(a):
  i = a.find('0')
  if i == -1:
    exit(a)
  for m in '3814697265625':
    okay = True
    for j in range(81):
      if (i-j)%9 == 0 or (i/9 == j/9) or (i/27 == j/27 and i%9/3 == j%9/3):
        if a[j] == m:
          okay = False
          break
    if okay:
      # At this point, m is not excluded by any row, column, or block, so let's place it and recurse
      r(a[:i]+m+a[i+1:])

r(argv[1])

ध्यान दें कि यदि कोई भी प्लेसमेंट काम नहीं करता है, तो आर वापस आ जाएगा और उस बिंदु पर वापस आ जाएगा, जहां कुछ और चुना जा सकता है, इसलिए यह एक बुनियादी गहराई पहले एल्गोरिथ्म है।

किसी भी आंकड़े का उपयोग नहीं करना, यह विशेष रूप से कुशल नहीं है। मैंने इस पहेली को विकिपीडिया ( http://en.wikipedia.org/wiki/Sudoku ) से लिया है :

$ time python sudoku.py 530070000600195000098000060800060003400803001700020006060000280000419005000080079
534678912672195348198342567859761423426853791713924856961537284287419635345286179

real    0m47.881s
user    0m47.223s
sys 0m0.137s

परिशिष्ट: मैं इसे एक रखरखाव प्रोग्रामर के रूप में कैसे लिखूंगा (इस संस्करण में लगभग 93x स्पीडअप है :)

import sys

def same_row(i,j): return (i/9 == j/9)
def same_col(i,j): return (i-j) % 9 == 0
def same_block(i,j): return (i/27 == j/27 and i%9/3 == j%9/3)

def r(a):
  i = a.find('0')
  if i == -1:
    sys.exit(a)

  excluded_numbers = set()
  for j in range(81):
    if same_row(i,j) or same_col(i,j) or same_block(i,j):
      excluded_numbers.add(a[j])

  for m in '123456789':
    if m not in excluded_numbers:
      # At this point, m is not excluded by any row, column, or block, so let's place it and recurse
      r(a[:i]+m+a[i+1:])

if __name__ == '__main__':
  if len(sys.argv) == 2 and len(sys.argv[1]) == 81:
    r(sys.argv[1])
  else:
    print 'Usage: python sudoku.py puzzle'
    print '  where puzzle is an 81 character string representing the puzzle read left-to-right, top-to-bottom, and 0 is a blank'

1
... जो सिर्फ यह दिखाने के लिए जाता है कि आप अभी भी अजगर में बुरा कोड लिख सकते हैं यदि आप वास्तव में कठिन प्रयास करते हैं :-)
जॉन फोउह

2
बस मुखरता के लिए, आप बदल सकते हैं i%9/3 == j%9/3करने के लिए (i%9) / 3 == (j%9) / 3। मुझे पता है कि आपको दिल से ऑपरेटरों के आदेश को जानना चाहिए, लेकिन इसे भूलना आसान है और इसे स्कैन करना थोड़ा आसान है।
जॉर्डन रीटर

1
क्या होगा अगर फंक्शन में पास किए गए नंबर गलत हैं? क्या यह हमेशा के लिए चला जाएगा या सभी संयोजनों की कोशिश के बाद यह खुद को समाप्त कर देगा?
गुंडर्स मैनेस

2
@ GundarsM Gness पुनरावृत्ति में प्रत्येक बिंदु पर, एक एकल खाली स्थिति को संभाला जाता है। यदि इस स्थिति के लिए कोई वैध अंक नहीं मिल सकता है, तो फ़ंक्शन बस वापस आ जाता है। इसका मतलब है, अगर पहले खाली स्थिति के लिए कोई वैध अंक नहीं मिल सकता है (यानी इनपुट एक अमान्य सुडोकू था) पूरे कार्यक्रम में आउटपुट के बिना रिटर्न होता है ( sys.exit(a)कभी नहीं पहुंचा जाता है)
मार्टिनस्टेनर

5
@JoshBibb मुझे पता है कि यह एक पुरानी पोस्ट है, लेकिन यह त्रुटि आपके लिए हो रही है क्योंकि यह Python2 के लिए लिखी गई थी और आप इसे Python3 में चला रहे हैं। सभी /ऑपरेटरों को same_row, same_colऔर ऑपरेटरों के same_blockसाथ बदलें //और आपको सही उत्तर मिलेगा।
एडम स्मिथ

10

इसे अनसुना करना:

def r(a):
    i = a.find('0') # returns -1 on fail, index otherwise
    ~i or exit(a) # ~(-1) == 0, anthing else is not 0
                  # thus: if i == -1: exit(a)
    inner_lexp = [ (i-j)%9*(i/9 ^ j/9)*(i/27 ^ j/27 | i%9/3 ^ j%9/3) or a[j] 
                   for j in range(81)]  # r appears to be a string of 81 
                                        # characters with 0 for empty and 1-9 
                                        # otherwise
    [m in inner_lexp or r(a[:i]+m+a[i+1:]) for m in'%d'%5**18] # recurse
                            # trying all possible digits for that empty field
                            # if m is not in the inner lexp

from sys import *
r(argv[1]) # thus, a is some string

इसलिए, हमें केवल आंतरिक सूची की अभिव्यक्ति की आवश्यकता है। मुझे पता है कि यह लाइन में निर्धारित अंकों को एकत्र करता है - अन्यथा, इसके आसपास के कोड का कोई मतलब नहीं है। हालांकि, मेरे पास कोई वास्तविक सुराग नहीं है कि यह कैसे होता है (और इम भी उस द्विआधारी कल्पना को काम करने के लिए थक गया है, क्षमा करें)


मैं एक अजगर विशेषज्ञ नहीं हूं, लेकिन लाइन 3 है या बाहर निकलें, इसलिए मुझे लगता है कि आपका तर्क उल्टा है
बॉबी जैक

मैं = -1 मान लें। तब ~ i = 0, और 0 या फू का मूल्यांकन करने के लिए फू का कारण बनता है। दूसरी ओर, अगर मैं! = -1, तो ~ मैं गैर-शून्य हो जाएगा, इस प्रकार, या का पहला भाग सत्य होगा, जिसके कारण दूसरे पैरामीटर का मूल्यांकन नहीं किया जा सकता है, जो कम परिचालित होने के कारण है मूल्यांकन।
टेटा १४'०

7

r(a)एक पुनरावर्ती कार्य है जो 0प्रत्येक चरण में बोर्ड में भरने का प्रयास करता है ।

i=a.find('0');~i or exit(a)सफलता की समाप्ति है। यदि 0बोर्ड में अधिक मूल्य मौजूद नहीं हैं, तो हम कर रहे हैं।

mवर्तमान मूल्य है जिसे हम भरने की कोशिश करेंगे 0

m in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3)or a[j]for j in range(81)]सत्य को मूल्यांकन करता है यदि इसे mचालू में रखना गलत है 0। चलो इसे "is_bad" उपनाम दें। यह सबसे मुश्किल बिट है। :)

is_bad or r(a[:i]+m+a[i+1:]एक सशर्त पुनरावर्ती कदम है। यह 0 बोर्ड में अगले मूल्यांकन के लिए पुनरावृत्ति करने की कोशिश करेगा यदि वर्तमान समाधान उम्मीदवार समझदार प्रतीत होता है।

for m in '%d'%5**18 सभी संख्याओं को 1 से 9 तक (अक्षम रूप से) शामिल करता है।


5

जब तक वे सफलतापूर्वक कोशिकाओं को भर नहीं पाते हैं, तब तक थोड़े से सुडोकू सॉल्वरों के प्रत्येक संभावित कानूनी संख्या को छोड़ दिया जाता है। मैंने इसे अलग नहीं किया है, लेकिन सिर्फ इसे छोड़ देना, ऐसा लगता है कि यह क्या करता है।


3

कोड वास्तव में काम नहीं करता है। आप स्वयं इसका परीक्षण कर सकते हैं। यहाँ एक नमूना अनसुलझी सुडोकू पहेली है:

80700000360208000000020090004000500100079800020010007000400300000000000040108300000506

आप इस वेबसाइट ( http://www.sudokuwiki.org/sudoku.htm ) का उपयोग कर सकते हैं , आयात पहेली पर क्लिक करें और बस उपरोक्त स्ट्रिंग की प्रतिलिपि बनाएँ। अजगर कार्यक्रम का आउटपुट है: 81731121362248232213122493444353544155579865526615677777466386998884718839997959596

जो समाधान के अनुरूप नहीं है। वास्तव में आप पहले से ही एक विरोधाभास, पहली पंक्ति में दो 1s देख सकते हैं।


1
अच्छी बात। आपने इस तरह की पहेली को कैसे प्रबंधित किया? क्या पहेली में किसी प्रकार की विशेषता है जो इस सॉल्वर को फेंकता है?
विले सलोनें

3
सावधान: यह पायथन 2.7 में लिखा गया था और जो सही प्रतिक्रिया देता है, वह है: 8974516236329874154152369877493258611637982542581643784695846137976542138321879546। पायथन 3 का उपयोग न करें क्योंकि विभाजन अलग हैं।
बीटा प्रोजेक्ट्स 20
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.