设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 5840|回复: 11

[胡扯] 蛋疼数学程序帝!猜数字解法

 关闭 [复制链接]

Lv3.寻梦者

梦石
0
星屑
1035
在线时间
1564 小时
注册时间
2008-7-30
帖子
4418

贵宾

发表于 2010-9-23 10:27:54 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 DeathKing 于 2010-9-24 12:57 编辑

  猜数字(又称 Bulls and Cows )是一种大概于20世纪中期兴起于英国的益智类小游戏。一般由两个人玩,也可以由一个人和电脑玩,在纸上、在网上都可以玩。这种游戏规则简单,但可以考验人的严谨和耐心。

  通常由两个人玩,一方出数字,一方猜。出数字的人要想好一个没有重复数字的4位数,不能让猜的人知道。猜的人就可以开始猜。每猜一个数字,出数者就要根据这个数字给出几A几B,其中A前面的数字表示位置正确的数的个数,而B前的数字表示数字正确而位置不对的数的个数。

  如正确答案为 5234,而猜的人猜 5346,则是 1A2B,其中有一个5的位置对了,记为1A,而3和4这两个数字对了,而位置没对,因此记为 2B,合起来就是 1A2B。

  接着猜的人再根据出题者的几A几B继续猜,直到猜中(即 4A0B)为止。

  假定正确答案为1234,则:
  0123 #=> 0A3B
  1423 #=> 1A3B
  1234 #=> 4A0B

  试找到一种行之有效的方法,使得程序在最短时间以及最小尝试次数内(不能超过10次)找到答案。

  可以是伪代码、Pascal语言、C/C++/C#语言、Basic语言、Ruby语言以及其他高级语言。并请各位自行编写生成正确答案与获得结果的函数。



1、百度百科,猜数字:http://baike.baidu.com/view/358630.htm
2、Mastermind - Wiki:http://en.wikipedia.org/wiki/Mastermind_(board_game)
3、Mastermind 解法综述:http://mathworld.wolfram.com/Mastermind.html
4、Knuth (1976). The Computer As Master Mind:http://www.dcc.fc.up.pt/~sssousa/RM09101.pdf
5、Kooi (2005). Yet Another Mastermind Strategy: http://www.philos.rug.nl/~barteld/master.pdf

点评

中秋快乐!  发表于 2010-9-23 10:36

See FScript Here:https://github.com/DeathKing/fscript
潜心编写URG3中。
所有对URG3的疑问和勘误或者建议,请移步至发布页面。
欢迎萌妹纸催更

Lv1.梦旅人

水土火风重逢处

梦石
0
星屑
229
在线时间
691 小时
注册时间
2010-7-17
帖子
3042
发表于 2010-9-23 10:29:04 | 显示全部楼层
这个我妈的手机有,可是我还是不明白游戏规则
独坐望城,望断天涯
回复 支持 反对

使用道具 举报

头像被屏蔽

Lv1.梦旅人 (禁止发言)

梦石
0
星屑
50
在线时间
66 小时
注册时间
2010-7-13
帖子
366
发表于 2010-9-23 10:31:49 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
462 小时
注册时间
2007-7-30
帖子
643
发表于 2010-9-23 22:42:33 | 显示全部楼层
出数字的人要想好一个没有重复数字的4位数 <===关键

不论 题目走访 或 答案走访 "最多"都要重复 4 * 4 = 16 次运算
不过因为上面的关键
所以只要这样就好

假C/C++ language... :
#include <stdio.h>
#include <stdlib.h>
char ques[] = "1234"
char ans[] = "0123";
int ans_l[10] = {-1};
int a = 0;
int b = 0;
int temp = 0;
for (int i = 0; i < 4; ++i)
  ans_l[atoi(ans)] = i; // atoi() convert char to int, and fetch answer's char into array list
// 差点就end了 = =|
for (int i = 0; i < 4; ++i)
  if ((temp = ans_l[atoi(ques)]) >= 0) // if the question's char is in the answer
    if (temp == i) // A
      ++a;
    else // B
      ++b;
printf("%dA%dB", a, b);

共 4 + 4 次 = =b
RGE这万年大坑 啥时填起来@@

回复 支持 反对

使用道具 举报

Lv3.寻梦者

孤独守望

梦石
0
星屑
3121
在线时间
1534 小时
注册时间
2006-10-16
帖子
4321

开拓者贵宾

发表于 2010-9-24 10:05:48 | 显示全部楼层
本帖最后由 IamI 于 2010-9-24 13:09 编辑

第三轮?继续决绝占楼
……最高次数不过5040次,毫无压力= =b
  1. # 假定持有判定函数 answer(num1,num2,num3,num4),返回数组[A,B]
  2. # start
  3. for i in 0..9
  4.         for j in 0..9
  5.                 for k in 0..9
  6.                         for l in 0..9
  7.                                 next if (i = j or i = k or i = l or j = k or j = l or k = l)
  8.                                 return 1000 * i + 100 * j + 10 * k + l if answer(i,j,k,l)[0] == 4
  9.                         end
  10.                 end
  11.         end
  12. end
复制代码

点评

其实这个题目是要求在10步以内解出 O_O  发表于 2010-9-24 12:56
菩提本非树,明镜本非台。回头自望路漫漫。不求姻缘,但求再见。
本来无一物,何处惹尘埃。风打浪吹雨不来。荒庭遍野,扶摇难接。
不知道多久更新一次的博客
回复 支持 反对

使用道具 举报

Lv1.梦旅人

彩色的银子

梦石
0
星屑
50
在线时间
190 小时
注册时间
2006-6-13
帖子
1361

贵宾

发表于 2010-9-24 12:05:15 | 显示全部楼层
楼上想复杂了。其实4个数可拆开,逐个判断。次数不超过4*10…

点评

0000~9999 10次,然后全排列4! = 24次,一共34……  发表于 2010-9-24 13:10
-.-
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
1
星屑
8895
在线时间
4368 小时
注册时间
2005-10-22
帖子
6742

开拓者贵宾

发表于 2010-9-24 12:08:08 | 显示全部楼层
蛋痛邪恶程序帝表示偶觉得直接找到存放答案的内存地址然后读它比较快(被PIA飞)
回复 支持 反对

使用道具 举报

Lv1.梦旅人

万物创造者

梦石
0
星屑
54
在线时间
352 小时
注册时间
2008-2-15
帖子
2432
发表于 2010-9-24 13:04:43 | 显示全部楼层
本帖最后由 小幽的马甲 于 2010-9-24 16:29 编辑
  1. class Guess
  2.   
  3.   def initialize(answer)
  4.     @ans = []
  5.     for i in 0..9999
  6.       @ans.push(i) if possible(i)
  7.     end
  8.     @s = answer
  9.   end
  10.   
  11.   def possible(i)
  12.     q = []
  13.     q[0..9] = 0
  14.     a = arr(i)
  15.     for n in a
  16.       return false if q[n] == 1
  17.       q[n] = 1
  18.     end
  19.   end

  20.   def arr(num)
  21.     a = []
  22.     for i in 0..3
  23.       a[3 - i] = num % 10
  24.       num /= 10
  25.     end
  26.     return a
  27.   end
  28.   
  29.   def check(a1, a2, a, b)
  30.     arr1 = arr(a1)
  31.     arr2 = arr(a2)
  32.     t = 0
  33.     for i in 0..3
  34.       t += 1 if arr1[i] == arr2[i]
  35.     end
  36.     return true if t != a
  37.     q = Array.new(10, 0)
  38.     t = 0
  39.     for i in arr1
  40.       q[i] = 1
  41.     end
  42.     for i in arr2
  43.       t += q[i]
  44.     end
  45.     return t != a + b
  46.   end

  47.   def ask(num,num2 = @s)
  48.     arr1 = arr(num2)
  49.     arr2 = arr(num)
  50.     a = 0
  51.     for i in 0..3
  52.       a += 1 if arr1[i] == arr2[i]
  53.     end
  54.     q = Array.new(10, 0)
  55.     b = -a
  56.     for i in arr1
  57.       q[i] = 1
  58.     end
  59.     for i in arr2
  60.       b += q[i]
  61.     end
  62.     return [a,b]
  63.   end

  64.   def getnum
  65.     ti = rand(@ans.size)
  66.     t = @ans.size
  67.     for o in 1..10
  68.       i1 = rand(@ans.size)
  69.       i = @ans[i1]
  70.       aa = 0
  71.       r1 = [1000/@ans.size].min
  72.       for r in 1..r1
  73.         j = @ans[rand(@ans.size)]
  74.         a, b = *ask(j,i)
  75.         s = @ans.size
  76.         for k in @ans
  77.           s -= 1 if check(j, k, a, b)
  78.         end
  79.         aa = s if s > aa
  80.       end
  81.       if aa <= t and aa > 0
  82.         t = aa
  83.         ti = i1
  84.       end
  85.     end
  86.     return ti
  87.   end
  88.   
  89.   def calc
  90.     time = 0
  91.     while @ans.size > 0
  92.       time += 1
  93.       i1 = ((time == 1) ? 123 : getnum)
  94.       i = @ans[i1]
  95.       a, b = *ask(i)
  96.       str = i.to_s
  97.       str = '0' + str if i < 1000
  98.       print "第#{time}次\n#{str}\n#{a}A#{b}B"
  99.       if a == 4
  100.         print "答案是#{str}"
  101.         return true
  102.       end
  103.       for num in [email protected]
  104.         @ans[num] = nil if check(i,@ans[num],a,b)
  105.       end
  106.       @ans[i1] = nil
  107.       @ans.compact!
  108.     end
  109.     p "Impossible!"
  110.     return false
  111.   end
  112. end

  113. guess = Guess.new(1234)
  114. guess.calc
复制代码
先丢半成品
关键在getnum这个方法上,先再想想= =

评分

参与人数 1星屑 +240 收起 理由
DeathKing + 240

查看全部评分

From mortal hope immortal power springs.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
462 小时
注册时间
2007-7-30
帖子
643
发表于 2010-9-25 10:04:32 | 显示全部楼层
啊~原来是找出答案阿

建表?
  1. $question = "5968"

  2. $table = array.new
  3. for i in 0..9
  4.   for j in 0..9
  5.     for k in 0..9
  6.       for l in 0..9
  7.         $table.push("#{i}#{j}#{k}#{l}") # 建表
  8.       end
  9.     end
  10.   end
  11. end

  12. def check(c)
  13.   a = b = 0
  14.   if (p = $question.match("["+c+"]")) != nil # 测试是否有A或B的情况
  15.     for i in p
  16.       if $question.index(i) == c.index(i)
  17.         a += 1
  18.       else
  19.         b += 1
  20.       end
  21.     end
  22.   end
  23.   return a,b
  24. end

  25. def guess(g)
  26.   a,b = check(g) # 检查?A?B
  27.   $table.collect! {|t| check(t) == a,b ? t : nil}
  28.   $table = $table.compact
  29. end

  30. guess_times = 4
  31. guess("1234")
  32. guess("3456")
  33. guess("5678")
  34. guess("7890")
  35. for i in $table
  36.   guess_times+=1
  37.   if guess(i) == 4,0 # 4A0B
  38.     p "Ans: #{i}\nGuess #{guess_times} times"
  39.   end
  40. end
复制代码
应该就这样 (汗

评分

参与人数 1星屑 +240 收起 理由
DeathKing + 240 可以考虑做个测试,

查看全部评分

RGE这万年大坑 啥时填起来@@

回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
发表于 2010-9-26 04:24:58 | 显示全部楼层
本帖最后由 紫苏 于 2010-9-26 04:42 编辑

BullsAndCowsServices 模块:提供 Bulls and Cows 游戏和破译器的一些通用服务;
BullsAndCows 是 Bulls and Cows 游戏的类,其实例是一次游戏的实例;
BullsAndCowsBreaker 是用来解 Bulls and Cows 的破译器的类;

  1. module BullsAndCowsServices
  2.   # 生成无重复数位的随机四位数
  3.   def generate_digits
  4.     arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
  5.     arr.slice!(rand(9))*1000 +
  6.     arr.slice!(rand(9))*100 +
  7.     arr.slice!(rand(8))*10 +
  8.     arr.slice!(rand(7))
  9.   end
  10.   # 把四位整数转换为数位数组
  11.   def digits2arr(digits)
  12.     return [digits%10, digits/10%10, digits/100%10, digits/1000]
  13.   end
  14.   # 返回猜测的结果
  15.   def judge(ans, num)
  16.     a = b = 0
  17.     ans_digs = digits2arr(ans)
  18.     my_digs = digits2arr(num)
  19.     for i in 0..3
  20.       if my_digs[i] == ans_digs[i]
  21.         a += 1
  22.       elsif ans_digs.include?(my_digs[i])
  23.         b += 1
  24.       end
  25.     end
  26.     return a*10+b
  27.   end
  28. end

  29. class BullsAndCows
  30.   include BullsAndCowsServices
  31.   attr_reader :step
  32.   # 初始化
  33.   def initialize
  34.     @num = generate_digits
  35.     @step = 0
  36.   end
  37.   # 猜一个数字,返回结果
  38.   def guess(num)
  39.     result = judge(@num, num)
  40.     @step += 1
  41.     p "guessing #{num}, (#{result/10}A#{result%10}B), step ##@step"
  42.     return result
  43.   end
  44. end


  45. class BullsAndCowsBreaker
  46.   include BullsAndCowsServices
  47.   # 初始化
  48.   def initialize(bac)
  49.     @bac = bac
  50.     @possible_guesses = []
  51.     for i in 1023..9876
  52.       ans_digs = digits2arr(i)
  53.       if ans_digs.uniq.length == ans_digs.length
  54.         @possible_guesses << i
  55.       end
  56.     end
  57.   end
  58.   # 排除不可能的数字
  59.   def eliminate(guess, result)
  60.     @possible_guesses.delete_if do |possible_guess|
  61.       indirect_result = judge(guess, possible_guess)
  62.       indirect_result/10 != result/10 || indirect_result%10 != result%10
  63.     end
  64.   end
  65.   # 开始破译
  66.   def start_guessing(best_guess = 1023)
  67.     loop do
  68.       result = @bac.guess(best_guess)
  69.       eliminate(best_guess, result)
  70.       break if result == 40
  71.       best_guess = @possible_guesses[rand(@possible_guess)]
  72.     end
  73.   end
  74. end
复制代码
实际测试时可以:
  1.   bac = BullsAndCows.new
  2.   bacb = BullsAndCowsBreaker.new(bac)
  3.   bacb.start_guessing
复制代码
使用的步数在 2-8 之间,实际上理论上可以是一步到位,取决于第一次猜的时候使用的随机数字,当然概率极小;宏观方向测试:

  1. TIMES = 1000
  2. best = 999
  3. worst = 1
  4. sum = 0
  5. TIMES.times do |i|
  6.   bac = BullsAndCows.new
  7.   bacb = BullsAndCowsBreaker.new(bac)
  8.   bacb.start_guessing
  9.   sum += bac.step
  10.   worst = bac.step if bac.step > worst
  11.   best = bac.step if bac.step < best
  12. end
  13. p "best:#{best},worst:#{worst},average:#{sum.to_f/TIMES}"
复制代码
输出:
best:2,worst:8,average:5.478
平均需要猜 5.478 次 ……|||
这里用的方法很弱智,每次猜完根据结果排除不可能是答案的数字,然后在剩下的数字里随机选一个……|| 如果精通博弈论、决策论的强人应该有更好的方法在每次排除之后找最有可能是答案的数字……||

点评

比如使用极小化最大值之类的 ||||  发表于 2010-9-26 04:28

评分

参与人数 1星屑 +480 收起 理由
DeathKing + 480 我每次人工做的时候,前四个全试1234、1342 ...

查看全部评分

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-18 14:41

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表