赞 | 2 |
VIP | 143 |
好人卡 | 1 |
积分 | 1 |
经验 | 216792 |
最后登录 | 2019-10-10 |
在线时间 | 24 小时 |
Lv1.梦旅人
- 梦石
- 0
- 星屑
- 61
- 在线时间
- 24 小时
- 注册时间
- 2008-8-5
- 帖子
- 1924
|
本帖最后由 紫苏 于 2010-9-26 04:42 编辑
BullsAndCowsServices 模块:提供 Bulls and Cows 游戏和破译器的一些通用服务;
BullsAndCows 是 Bulls and Cows 游戏的类,其实例是一次游戏的实例;
BullsAndCowsBreaker 是用来解 Bulls and Cows 的破译器的类;
- module BullsAndCowsServices
- # 生成无重复数位的随机四位数
- def generate_digits
- arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
- arr.slice!(rand(9))*1000 +
- arr.slice!(rand(9))*100 +
- arr.slice!(rand(8))*10 +
- arr.slice!(rand(7))
- end
- # 把四位整数转换为数位数组
- def digits2arr(digits)
- return [digits%10, digits/10%10, digits/100%10, digits/1000]
- end
- # 返回猜测的结果
- def judge(ans, num)
- a = b = 0
- ans_digs = digits2arr(ans)
- my_digs = digits2arr(num)
- for i in 0..3
- if my_digs[i] == ans_digs[i]
- a += 1
- elsif ans_digs.include?(my_digs[i])
- b += 1
- end
- end
- return a*10+b
- end
- end
- class BullsAndCows
- include BullsAndCowsServices
- attr_reader :step
- # 初始化
- def initialize
- @num = generate_digits
- @step = 0
- end
- # 猜一个数字,返回结果
- def guess(num)
- result = judge(@num, num)
- @step += 1
- p "guessing #{num}, (#{result/10}A#{result%10}B), step ##@step"
- return result
- end
- end
- class BullsAndCowsBreaker
- include BullsAndCowsServices
- # 初始化
- def initialize(bac)
- @bac = bac
- @possible_guesses = []
- for i in 1023..9876
- ans_digs = digits2arr(i)
- if ans_digs.uniq.length == ans_digs.length
- @possible_guesses << i
- end
- end
- end
- # 排除不可能的数字
- def eliminate(guess, result)
- @possible_guesses.delete_if do |possible_guess|
- indirect_result = judge(guess, possible_guess)
- indirect_result/10 != result/10 || indirect_result%10 != result%10
- end
- end
- # 开始破译
- def start_guessing(best_guess = 1023)
- loop do
- result = @bac.guess(best_guess)
- eliminate(best_guess, result)
- break if result == 40
- best_guess = @possible_guesses[rand(@possible_guess)]
- end
- end
- end
复制代码 实际测试时可以:- bac = BullsAndCows.new
- bacb = BullsAndCowsBreaker.new(bac)
- bacb.start_guessing
复制代码 使用的步数在 2-8 之间,实际上理论上可以是一步到位,取决于第一次猜的时候使用的随机数字,当然概率极小;宏观方向测试:
- TIMES = 1000
- best = 999
- worst = 1
- sum = 0
- TIMES.times do |i|
- bac = BullsAndCows.new
- bacb = BullsAndCowsBreaker.new(bac)
- bacb.start_guessing
- sum += bac.step
- worst = bac.step if bac.step > worst
- best = bac.step if bac.step < best
- end
- p "best:#{best},worst:#{worst},average:#{sum.to_f/TIMES}"
复制代码 输出:
best:2,worst:8,average:5.478
平均需要猜 5.478 次 ……|||
这里用的方法很弱智,每次猜完根据结果排除不可能是答案的数字,然后在剩下的数字里随机选一个……|| 如果精通博弈论、决策论的强人应该有更好的方法在每次排除之后找最有可能是答案的数字……|| |
评分
-
查看全部评分
|