用Ruby amb解决一个puzzle

请回答下面10个问题,各题都恰有一个答案是正确的:
(1)第一个答案是B的问题是哪一个?
A、2      B、3      C、4      D、5        E、6

(2)恰好有两个连续问题的答案是一样的,它们是:
A、2,3      B、3,4      C、4,5      D、5,6      E、6,7

(3)本问题答案和哪一个问题的答案相同?
A、1      B、2      C、4      D、7     E、6

(4)答案是A的问题的个数是:
A、0       B、1       C、2       D、3     E、4

(5)本问题答案和哪一个问题的答案相同?
A、10     B、9       C、8        D、7     E、6

(6)答案是A的问题的个数和答案是什么的问题的个数相同?
A、B      B、C       C、D         D、E     E、以上都不是

(7)按照字母顺序,本问题的答案和下一个问题的答案相差几个字母?
A、4       B、3      C、2          D、1      E、0(注:A和B相差一个字母)

(8)答案是元音字母的问题的个数是:
A、2       B、3       C、4         D、5        E、6(注:A和E是元音字母)

(9)答案是辅音字母的问题的个数是:
A、一个质数       B、一个阶乘数       C、一个平方数       D、一个立方数       E、5的倍数

(10)本问题的答案是:
A、A       B、B         C、C        D、D        E、E

Ruby语言基于amb的解决方案,本质上,这是一个backtrack search:

#  A B C D E
#
 0 1 2 3 4 
class  ConstraintChecker
  
def  initialize
    @constraints 
=  [
     
#  each constraint lambda expression should return false
      #  if the corresponding answer(the "this_answer" param) 
      #  is violated by answers seen so far(the "ans" param), 
      #  and true if the answer is satisfied or can not be judged yet
      # ~ lambda {|ans, this_answer| #test code
          # ~ a = [1, 1, 2, 3, 1, 2, 3, 4, 3, 1]

         
# ~ 0.upto(ans.length - 1) {|i|
            # ~ break false if ans[i] && ans[i]!=a[i] 
          # ~ } != false # true if break not executed
       # ~ },
       # q1
       lambda  { | ans, this_answer |
        firstB 
=  [ 1 , 2 , 3 , 4 , 5 ][this_answer]
        ans[firstB] 
==  nil  or  ans.index( 1 ==  firstB
      },
      
# q2
       lambda  { | ans, this_answer |
        idx 
=  [ 1 , 2 , 3 , 4 , 5 ][this_answer]
        
#  ans[idx] is the same as ans[idx+1]
        0.upto(ans.size - 2 ) { | i |
          
# ~ puts "i=#{i}, idx=#{idx}, ans[i]=#{ans[i]}, ans[i+1]=#{ans[i+1]}"
           break  true  if  ans[i + 1 ==  nil
          
break  false  if  ((i == idx)  !=  (ans[i] == ans[i + 1 ]))
        } 
!=  false
        
# ~ ans[idx]==nil or ans[idx]==ans[idx-1]
      },
      
# q3
       lambda  { | ans, this_answer |
        same 
=  [0,  1 3 6 5 ][this_answer]
        (ans[same] 
==  nil)  or  (ans[same]  ==  this_answer)
      },
      
# q4
       lambda  { | ans, this_answer |
        numOfA 
=  [0,  1 2 4 5 ][this_answer]
        (ans.last 
==  nil)  or  (ans.select{ | i | i == 0}.size  ==  numOfA)
      },
      
# q5
       lambda  { | ans, this_answer |
        same 
=  [ 9 8 7 6 5 ][this_answer]
        (ans[same] 
==  nil)  or  (ans[same]  ==  this_answer)
      },
      
# q6
       lambda  { | ans, this_answer |
        
return  true  if  ans.last  ==  nil
        
#        A, B, C, D, E
        nums  =  [0, 0, 0, 0, 0]
        [0, 
1 2 3 4 ].each do  | i |
          ans.each do 
| j |
            nums[i] 
+=   1   if  i == j
          end
        end
        
        idToA 
= []
        
1 .upto nums.size - 1  do  | i |
          idToA 
<<  i  if  nums[i]  ==  nums[0]
        end
        
        (idToA.empty? 
and  this_answer  ==   4 ||  (idToA.size  ==   1   and  idToA[0]  ==  this_answer + 1 )
        
      },
      
# q7
       lambda  { | ans, this_answer |
        nextAnswer 
=  ans[ 7 ]  
        
return  true  if  nextAnswer  ==  nil
        [
4 3 2 1 , 0][this_answer]  ==  (this_answer  -  nextAnswer).abs
      },
      
# q8
       lambda  { | ans, this_answer |
        
return  true  if  ans.last  ==  nil
        ans.select{
| i |  i == or  i == 4 }.size  ==  [ 2 , 3 , 4 , 5 , 6 ][this_answer]
      },
      
# q9
       lambda  { | ans, this_answer |
        
return  true  if  ans.last  ==  nil
        numCon 
=  ans.select{ | i |  i == 1   or  i == 2   or  i == 3 }.size
        
        conds 
=  [[ 2 3 5 7 ], [ 1 2 6 ], [ 1 4 9 ], [ 1 8 ], [0,  5 10 ]]
        conds.each_index do 
| i |
          
break  false  if  (i  ==  this_answer)  !=  (conds[i].include? numCon)
        end 
!=  false
      },
      
# q10
       lambda  { | ans, this_answer |
        true
      }
    ]
  end
  
  
def  Check(answers, upto = nil)
    
if  (upto != nil)  
      (upto
+ 1 ).upto answers.size - 1  do  | idx |
        answers[idx]
= nil
      end
    end
    
# ~ print "Checking "; dump answers; puts
    answers.each_index do  | i |
      
break  unless answers[i] 
      
return  false unless @constraints[i].call(answers, answers[i])
    end
  
    true
  end 
# of method Check
  
  
def  num_constraint
    @constraints.size
  end
  
  
#  exposion for debug purpose
  attr_reader :constraints
end 
# of class ConstraintChecker

class  AmbSolver

  
def  initialize
    @checker 
=  ConstraintChecker.new
    @result 
=  Array.new
  end
  
  
def  solve
    require 
' amb '
    a 
=  Amb.new
    begin
      answers  =  Array.new @checker.num_constraint
      answers.each_index do 
| i |
        next_answers 
=  (0.. 4 ).to_a.collect{ | n |  answers[i] = n; answers.dup}
        answers 
=  a.choose( * next_answers)
        a.
assert  @checker.Check(answers)
      end
      @result 
<<  answers.dup
      a.failure
    rescue Amb::ExhaustedError
    end
    @result
  end

end 
# of class AmbSolver

def  dump answers
  answers.each do 
| a |
    (a
== nil)?  print ( ' ? ' ) :  print (( ' A ' .. ' Z ' ).to_a[a])
    
print   "   "
  end
end

if   __FILE__   ==  $0
  puts 
" Consistent answers: "
  start 
=  Time.now
  result 
=  AmbSolver.new.solve
  elapsed 
=  Time.now  -  start
  result.each do 
| a |
    dump a
    puts
  end
  puts(
" time  " , elapsed)  # 0.468
end
其中amb.rb库是Jim Weirich用continuation写出来的,我改动了一点:
#  Modified Amb, originally by Jim Weirich
class  Amb
    
class  ExhaustedError  <  RuntimeError; end
    
    
def  initialize
        @fail 
=  proc { fail ExhaustedError,  " amb tree exhausted "  }
    end
    
    
def  choose( * choices)
        prev_fail 
=  @fail
        choices.each { 
| choice |
            callcc { 
| fk |
                @fail 
=  proc {
                    @fail 
=  prev_fail
                    fk.call(:fail)
                }
                
if  choice.respond_to? :call
                    
return  choice.call
                
else
                    
return  choice
                 end
            }
        }
        @fail.call

    end
    
    
def  failure
        choose
    end
    
    
def   assert (cond)
        failure unless cond
    end
end


你可能感兴趣的:(Ruby)