你是那个吗?(策划导数)


15

我有一个艰难的人给你!

我的女友最近在美国MTV上观看了一场新节目。这是一场可怕的演出,每个人都很垃圾,但是“游戏”非常有趣。从维基百科:

你是那个吗?跟随20位在夏威夷生活在一起的人,寻找他们的完美搭档。如果这10位男性和10位女性能够在10周内正确选择全部十场完美比赛,他们将获得100万美元的奖金。

现在是游戏部分(也来自维基百科):

演员们将在每集节目中与他们认为自己的绝配将在挑战中配对。挑战赛的获胜者将约会,并有机会在真相摊位中测试他们的比赛。主持人将选择其中一名获奖夫妇前往真相摊位,以确定他们是否完美匹配。 这是确认匹配的唯一方法。每个插曲都以配对仪式结束,在配对仪式上,夫妻会被告知他们有多少场完美的比赛,但不是正确的比赛。

TL; DR: 这是Mastermind的衍生物(具体来说,是M(10,10))。游戏规则如下:

  1. 您从2组10组开始,我们称它们为A组:{A,B,C,D,E,F,G,H,I,J}和Set 2:{1,2,3,4,5, 6,7,8,9,10}

  2. 计算机以{A1,B2,C3,D4,E5,F6,G7,H8,I9,J10}的形式创建一个对您不可见的解决方案,其中A组中的成员一对一映射设置2。解决方案的另一个示例可以是{A2,B5,C10,D8,E1,F7,G6,H4,I9,J3}。

  3. 在您的第一回合之前,您需要询问您选择的一对特定配对是否正确。您的问题将采用{A1}的形式(例如{C8}),并且您会收到1(表示正确)或0(表示您的猜测不正确)。

  4. 您的第一个实际回合。您首先以{A1,B2,C3,D4,E5,F6,G7,H8,I9,J10}的形式或您选择的任何排列进行猜测。 您的猜测不能包含任何项目的倍数,即{A1,A2,A3,A4,A5,B6,B7,B8,B9,B10}的猜测不是有效的猜测。每次转动之后,计算机都会告诉您正确的匹配次数,但不会告诉您哪些匹配正确。

  5. 重复第3步和第4步,直到您正确地确定每个比赛(即响应为10),或者直到您的10步上升(以较早者为准)。如果您在第10回合之前或第10回合中解决此问题,您将赢得100万美元。否则,你输了,有些人(或字母和数字)独自一人回家与他们的10只猫度过永恒。

这不是最短的代码竞赛。能够以最少的平均猜测数量解决随机匹配问题的人将是获胜者。聪明的游戏玩法和计算速度也可能是因素。我假设平均转数几乎肯定会大于10,因此您赢得100万美元奖金(大概是由MTV而非我支付)的几率很小。只是怎么不可能是它的演员赢得大奖?

注意:不一定需要将其放入{A1,B2,...}格式。我只是在问题中使用了该表格,以使其完全清楚其谜题是什么。如果您不采用这种形式,请说明如何调用。

祝好运!


3
如果你想谁可以解决其在至少平均猜测取胜的人,为什么不把获胜标准是什么?当完全有效的获胜条件盯着我们时,我看不出有什么理由应该成为人气竞赛。
Geobits

据我所知,这个问题与最佳玩Mastermind无关。它要求一个允许用户玩的游戏。
feersum 2014年

1
那么pop-contest不是这个的标签。

1
@ hosch250更新获奖者标准
dberm22

2
7个赞,2个收藏夹,没有答案。我知道这是艰难的!
dberm22

Answers:


6

Python 2(如果使用Pypy运行,运行速度更快)

相信几乎总是在10轮或更短的时间内猜对配对

我的算法来自我对策划者的回答,这是我的爱好(请参阅Ideone中的内容)。这样做的目的是找到使最坏情况下剩下的可能性最小化的猜测。我下面的算法只是蛮力,但是为了节省时间,如果剩余的可能性数大于,它只是随机选择RANDOM_THRESHOLD。您可以使用此参数来加快速度或查看更好的性能。

该算法非常慢,如果使用Pypy运行一次,平均运行10秒钟(如果使用普通的CPython解释器,则约为30秒钟),因此我无法对整个排列进行测试。但是性能还是不错的,经过大约30次测试后,我还没有发现在10轮或更短的时间内找不到正确配对的任何实例。

无论如何,如果在现实生活中使用此算法,则在下一轮之前有足够的时间(一个星期?),因此该算法可以在现实生活中使用= D

因此,我认为可以肯定地说,平均而言,这将在10个猜测或更少的猜测中找到正确的配对。

自己尝试。我可能会在接下来的几天里提高速度(编辑:似乎很难进一步提高,因此我将代码保留原样。我尝试仅进行随机选择,但即使在size=7,它在5040个案例中有3个都失败了,因此我决定保留更巧妙的方法)。您可以将其运行为:

pypy are_you_the_one.py 10

或者,如果您只是想看看它是如何工作的,请输入较小的数字(以便它运行更快)

要进行全面测试(警告: size > 7),请输入负数。

全面测试 size=7(在2m 32s中完成):

...
(6、5、4、1、3、2、0):5个猜测
(6、5、4、2、0、1、3):5个猜测
(6、5、4、2、0、3、1):4个猜测
(6,5,4,2,1,0,3):5个猜测
(6、5、4、2、1、3、0):6个猜测
(6、5、4、2、3、0、1):6个猜测
(6、5、4、2、3、1、0):6个猜测
(6、5、4、3、0、1、2):6个猜测
(6、5、4、3、0、2、1):3个猜测
(6,5,4,3,1,0,2):7个猜测
(6,5,4,3,1,2,0):7个猜测
(6、5、4、3、2、0、1):4个猜测
(6,5,4,3,2,1,0):7个猜测
平均计数:5.05
最大数量:7
最小数量:1
成功数:5040

如果RANDOM_THRESHOLDCLEVER_THRESHOLD都设置为非常高的值(例如50000),则将迫使算法找到最佳猜测,以使最坏情况下的可能性最小化。这很慢,但是功能很强大。例如,在其上运行将size=6断言它最多可以在5个回合中找到正确的配对。

尽管与使用近似值(平均为4.11轮)相比,该平均值更高,但它始终会成功,甚至还有一轮还可以保留。这进一步加强了我们的假设,即时size=10,它几乎总是应该在10轮或更短的时间内找到正确的配对。

结果(在3m 9s中完成):

(5、4、2、1、0、3):5个猜测
(5、4、2、1、3、0):5个猜测
(5、4、2、3、0、1):4个猜测
(5、4、2、3、1、0):4个猜测
(5、4、3、0、1、2):5个猜测
(5、4、3、0、2、1):5个猜测
(5、4、3、1、0、2):5个猜测
(5、4、3、1、2、0):5个猜测
(5、4、3、2、0、1):5个猜测
(5,4,3,2,1,0):5个猜测
平均计数:4.41
最大数量:5
最小数量:1
成功人数:720

代码。

from itertools import permutations, combinations
import random, sys
from collections import Counter

INTERACTIVE = False
ORIG_PERMS = []
RANDOM_THRESHOLD = 100
CLEVER_THRESHOLD = 0

class Unbuffered():
    def __init__(self, stream):
        self.stream = stream

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()

    def __getattr__(self, attr):
        self.stream.getattr(attr)
sys.stdout = Unbuffered(sys.stdout)

def init(size):
    global ORIG_PERMS
    ORIG_PERMS = list(permutations(range(size)))

def evaluate(solution, guess):
    if len(guess) == len(solution):
        cor = 0
        for sol, gss in zip(solution, guess):
            if sol == gss:
                cor += 1
        return cor
    else:
        return 1 if solution[guess[0]] == guess[1] else 0

def remove_perms(perms, evaluation, guess):
    return [perm for perm in perms if evaluate(perm, guess)==evaluation]

def guess_one(possible_perms, guessed_all, count):
    if count == 1:
        return (0,0)
    pairs = Counter()
    for perm in possible_perms:
        for pair in enumerate(perm):
            pairs[pair] += 1
    perm_cnt = len(possible_perms)
    return sorted(pairs.items(), key=lambda x: (abs(perm_cnt-x[1]) if x[1]<perm_cnt else perm_cnt,x[0]) )[0][0]

def guess_all(possible_perms, guessed_all, count):
    size = len(possible_perms[0])
    if count == 1:
        fact = 1
        for i in range(2, size):
            fact *= i
        if len(possible_perms) == fact:
            return tuple(range(size))
        else:
            return tuple([1,0]+range(2,size))
    if len(possible_perms) == 1:
        return possible_perms[0]
    if count < size and len(possible_perms) > RANDOM_THRESHOLD:
        return possible_perms[random.randint(0, len(possible_perms)-1)]
    elif count == size or len(possible_perms) > CLEVER_THRESHOLD:
        (_, next_guess) = min((max(((len(remove_perms(possible_perms, evaluation, next_guess)), next_guess) for evaluation in range(len(next_guess))), key=lambda x: x[0])
                               for next_guess in possible_perms if next_guess not in guessed_all), key=lambda x: x[0])
        return next_guess
    else:
        (_, next_guess) = min((max(((len(remove_perms(possible_perms, evaluation, next_guess)), next_guess) for evaluation in range(len(next_guess))), key=lambda x: x[0])
                               for next_guess in ORIG_PERMS if next_guess not in guessed_all), key=lambda x: x[0])
        return next_guess

def main(size=4):
    if size < 0:
        size = -size
        init(size)
        counts = []
        for solution in ORIG_PERMS:
            count = run_one(solution, False)
            counts.append(count)
            print '%s: %d guesses' % (solution, count)
        sum_count = float(sum(counts))
        print 'Average count: %.2f' % (sum_count/len(counts))
        print 'Max count    : %d' % max(counts)
        print 'Min count    : %d' % min(counts)
        print 'Num success  : %d' % sum(1 for count in counts if count <= size)
    else:
        init(size)
        solution = ORIG_PERMS[random.randint(0,len(ORIG_PERMS)-1)]
        run_one(solution, True)

def run_one(solution, should_print):
    if should_print:
        print solution
    size = len(solution)
    cur_guess = None
    possible_perms = list(ORIG_PERMS)
    count = 0
    guessed_one = []
    guessed_all = []
    while True:
        count += 1
        # Round A, guess one pair
        if should_print:
            print 'Round %dA' % count
        if should_print:
            print 'Num of possibilities: %d' % len(possible_perms)
        cur_guess = guess_one(possible_perms, guessed_all, count)
        if should_print:
            print 'Guess: %s' % str(cur_guess)
        if INTERACTIVE:
            evaluation = int(raw_input('Number of correct pairs: '))
        else:
            evaluation = evaluate(solution, cur_guess)
            if should_print:
                print 'Evaluation: %s' % str(evaluation)
        possible_perms = remove_perms(possible_perms, evaluation, cur_guess)

        # Round B, guess all pairs
        if should_print:
            print 'Round %dB' % count
            print 'Num of possibilities: %d' % len(possible_perms)
        cur_guess = guess_all(possible_perms, guessed_all, count)
        if should_print:
            print 'Guess: %s' % str(cur_guess)
        guessed_all.append(cur_guess)
        if INTERACTIVE:
            evaluation = int(raw_input('Number of correct pairs: '))
        else:
            evaluation = evaluate(solution, cur_guess)
            if should_print: print 'Evaluation: %s' % str(evaluation)
        if evaluation == size:
            if should_print:
                print 'Found %s in %d guesses' % (str(cur_guess), count)
            else:
                return count
            break
        possible_perms = remove_perms(possible_perms, evaluation, cur_guess)

if __name__=='__main__':
    size = 4
    if len(sys.argv) >= 2:
        size = int(sys.argv[1])
        if len(sys.argv) >= 3:
            INTERACTIVE = bool(int(sys.argv[2]))
    main(size)

这真是不可思议。我已经运行了100次以上,但要找到解决方案还需要十多个猜测。我看过几个10,甚至几个6。(您说您还没有看到在10个回合以下找不到正确配对的任何实例。应该说“不超过10个回合”,但这只是语义。)这太棒了!我假设您的lambda值是某种熵的度量,可以让您做出最佳猜测,但是我看不到它的设置方式或位置。这只是我的专长,而不是对您程序的起诉。难以置信的工作!
dberm22 2014年

它正在尝试使最坏情况(len(remove_perms ...)部分)中剩下的可能性最小化。是的,我的意思是<= 10轮=)。顺便说一句,因为我放了,所以从来没有做出过真正最佳猜测的代码CLEVER_THRESHOLD=0,这意味着它只会尝试从可能性列表中进行猜测,尽管最佳猜测可能不在此范围之内。但是我决定禁用它以节省时间。我为添加了完整的测试size=7,表明它总是成功。
justhalf 2014年

1
我一直在用“聪明的阈值= 0”(从(9,8,7,6,5,4,3,2,1,0)开始并在所有排列中反向工作)过夜运行您的代码。到目前为止,我只有2050年,但是已经有13个案例发生了11个回合。样本打印输出-(9、8、7、4、0、6、3、2、1、5):9个猜测,平均计数:8.29,最大计数:11,最小计数:4,成功次数:2037,成功次数评估:2050。如果正确设置了“智能阈值”,我敢打赌那11会变成10。平均而言,8.3仍然非常出色。
dberm22 2014年

@ dberm22:是的,谢谢您在一夜之间运行这种缓慢的算法!我进行了全面测试size=8,发现如果使用此设置,则最新版本只有40308成功(而不是40320)。但这仍然是99.97%的成功率!猜猜我们可以让电视节目主持人破产。
justhalf 2014年

3

CJam -19 Turns-白痴的策略

这不是一个认真的答案,而是一个示范。这是一个白痴的解决方案,他没有考虑转弯第二部分提供的正确配对信息的数量。使用完全随机的配对,平均需要27周。正如我所说的那样,这个答案是不够的,但是它表明,一个聪明的群体(这个程序更聪明)的几率可能不如您期望的那么渺茫。我编写的算法更加智能,但是要花费更多的时间才能运行,因此我实际上可以从中获得答案。

更新:下面的代码已更新为使用状态,如果唯一正确的代码是我们已经知道正确的代码,则应删除不起作用的代码。还对其进行了编辑,以显示我的随机“正确答案”生成器。现在的平均结果仅为19。这仍然是一个愚蠢的解决方案,但比以前的解决方案要好一些。

A,{__,mr=_@@-}A*;]sedS*:Z;

ZS/:i:G;                               "Set the input (goal) to G";
{UU@{G2$==@+\)}%~;}:C;                 "This is the tool to count how many of an array agree with G";
{:Y;1$1$<{Y-}%Yaa+@@)>{Y-}%+}:S;       "for stack A X Y, sets the Xth value in the array to Y";
{:Y;1$1$<2$2$=Y-a+@@)>+}:R;            "for stack A X Y, removes Y from the Xth value in the array";

1:D;                                   "Set turn counter to one. if zero exits loop";

A,]A*                                  "array of arrays that has all possible values for an ordering";

{                                      "start of loop";

_V=(\;_GV=={V\SV):V;}{V\R}?            "Guesses a number for the first unknown. If right sets the pair; else erases it";

_[{(_,_{mr=}{;;11}?:Y\{Y-}%}A*;]_C     "guesses random possible arrangement and determines how many are right, error=11";
\_{+}*45-:Y{Y;{_11={;BY-}{}?}%}{}?\    "error correct by including the missing number";

_V={;V:X>{X\RX):X;}%~LV}{}?            "if all new are wrong, makes sure they aren't guessed again";
_A={Dp0:D;;p;}{D):D;;;}?               "If all are right, prints it an tells loop to exit.  Else increments counter";

D}g                                    "repeat from start of loop";

另请注意:草率的错误处理是因为比起更智能的方法更容易编程。
kaine 2014年

+1足以实际勇于实现解决方案。实际上我很震惊,平均只需要27圈就可以猜出正确的解决方案。我假设您猜对了,以后的匹配更容易按指数形式(好,因果地)找到。谢谢!我很想知道是否有人能获得少于10的收益。您给了我希望!
dberm22

如果27令人惊讶,那就看看吧!除了开玩笑之外,我认为有可能保证10个或至少使它平均的解决方案。我只是无法在合理的时间范围内使用这种算法。
kaine 2014年

19 ...我更加震惊。不过,这只是一个问题……在程序中,您说“为第一个未知数猜测一个数字。如果设置正确,则删除该对;否则删除它”。如果错了...您应该将其添加到您知道不正确的匹配项列表中,这样就不必再猜测了(在排列或单独猜测中)。
dberm22

这意味着从可能性列表中删除它;我有一个可能的列表,而不是一个不可能的列表。哦,我忘了提到这在数组中有一个男性位置,而女性则是数字0-9(反之亦然)。如果是更认真的提交,我会使用A5 B2等格式。
kaine 2014年

3

快速多线程C ++版本

我知道已经有一段时间了,因为该线程处于活动状态,但我还有一个很酷的分享:这是Mini Are算法的C ++实现,它是《一个人吗?,它使用多线程来加快对每个可能猜测的评估。

此版本比Python版本快得多(当原始Python版本设置为maximum RANDOM_THRESHOLD和时,速度要快100倍以上CLEVER_THRESHOLD)。它不使用任何随机猜测,而是评估所有排列并提交消除最大可能解决方案(考虑到最坏情况的响应)的排列作为其猜测。

对于较小的游戏,调用“ ayto -n”将在所有n上运行游戏!可能隐藏的匹配项,最后将为您提供简短的结果摘要。

由于评估所有10个仍然很棘手!可能的隐藏匹配,例如,如果您调用“ ayto 10”,则模拟器会进行固定的前三个猜测,然后运行minimax选择其猜测,并假定已给出最坏情况的评估。这将我们引向“最坏情况路径”,指向隐藏的矢量,该矢量可能属于矢量类别,该类别使算法能够识别出最多的猜测。这个猜想尚未得到检验。

直到n = 9为止,还没有一个模拟能够解决n匝以上的问题。

为了自己进行测试,示例编译如下:

g++ -std=c++11 -lpthread -o ayto ayto.cpp

这是一个带有输出的小示例:

$ ./ayto -4
Found (0, 1, 2, 3) in 2 guesses.
Found (0, 1, 3, 2) in 3 guesses.
Found (0, 2, 1, 3) in 2 guesses.
Found (0, 2, 3, 1) in 3 guesses.
Found (0, 3, 1, 2) in 2 guesses.
Found (0, 3, 2, 1) in 2 guesses.
Found (1, 0, 2, 3) in 1 guesses.
Found (1, 0, 3, 2) in 3 guesses.
Found (1, 2, 0, 3) in 3 guesses.
Found (1, 2, 3, 0) in 3 guesses.
Found (1, 3, 0, 2) in 3 guesses.
Found (1, 3, 2, 0) in 3 guesses.
Found (2, 0, 1, 3) in 3 guesses.
Found (2, 0, 3, 1) in 3 guesses.
Found (2, 1, 0, 3) in 3 guesses.
Found (2, 1, 3, 0) in 3 guesses.
Found (2, 3, 0, 1) in 3 guesses.
Found (2, 3, 1, 0) in 3 guesses.
Found (3, 0, 1, 2) in 3 guesses.
Found (3, 0, 2, 1) in 3 guesses.
Found (3, 1, 0, 2) in 3 guesses.
Found (3, 1, 2, 0) in 3 guesses.
Found (3, 2, 0, 1) in 3 guesses.
Found (3, 2, 1, 0) in 3 guesses.
***** SUMMARY *****
Avg. Turns: 2.75
Worst Hidden Vector: (0, 1, 3, 2) in 3 turns.

/* Multithreaded Mini-max Solver for MTV's Are You The One? */

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <algorithm>
#include <numeric>
#include <string>
#include <vector>
#include <map>
#include <thread>
#include <cmath>

#define TEN_FACT (3628800)
#define NUM_CHUNKS (8)

using std::cout;
using std::cin;
using std::endl;
using std::vector;
using std::string;
using std::map;
using std::pair;
using std::find;
using std::abs;
using std::atoi;
using std::next_permutation;
using std::max_element;
using std::accumulate;
using std::reverse;
using std::thread;

struct args {
    vector<string> *perms;
    vector<string> *chunk;
    pair<string, int> *cd;
    int thread_id;
};

void simulate_game(const string &hidden, map<string, int> &turns_taken,
                   bool running_all);
bool picmp(const pair<string, int> &p1, const pair<string, int> &p2);
double map_avg(const map<string, int> &mp);
int nrand(int n);
int evaluate(const string &sol, const string &query);
vector<string> remove_perms(vector<string> &perms, int eval, string &query);
pair<string, int> guess_tb(vector<string> &perms, vector<string> &guessed_tb, int turn);
pair<string, int> guess_pm(vector<string> &perms, vector<string> &guessed, int turn);
void make_chunks(vector<string> &orig, vector<vector<string> > &chunks, int n);
string min_candidate(pair<string, int> *candidates, int n);
void get_score(struct args *args);
int wc_response(string &guess, vector<string> &perms);
bool prcmp(pair<int, int> x, pair<int, int> y);
void sequence_print(string s);
struct args **create_args(vector<string> &perms, pair<string, int> *cd, vector<string> &chunk, int thread_id);


vector<string> ORIGPERMS;

int main(int argc, char **argv)
{
    int sz;
    map<string, int> turns_taken;
    const string digits = "0123456789";
    bool running_all = false;

    if (argc != 2) {
        cout << "usage: 'ayto npairs'" << endl;
        return 1;
    } else {
        if ((sz = atoi(argv[1])) < 0) {
            sz = -sz;
            running_all = true;
        }
        if (sz < 3 || sz > 10) {
            cout << "usage: 'ayto npairs' where 3 <= npairs <= 10" << endl;;
            return 1;
        }
    }

    // initialize ORIGPERMS and possible_perms
    string range = digits.substr(0, sz);
    do {
        ORIGPERMS.push_back(range);
    } while (next_permutation(range.begin(), range.end()));

    if (running_all) {
        for (vector<string>::const_iterator it = ORIGPERMS.begin();
             it != ORIGPERMS.end(); ++it) {
            simulate_game(*it, turns_taken, running_all);
        }
        cout << "***** SUMMARY *****\n";
        cout << "Avg. Turns: " << map_avg(turns_taken) << endl;
        pair<string, int> wc = *max_element(turns_taken.begin(),
                                            turns_taken.end(), picmp);
        cout << "Worst Hidden Vector: ";
        sequence_print(wc.first);
        cout << " in " << wc.second << " turns." << endl;
    } else {
        string hidden = ORIGPERMS[nrand(ORIGPERMS.size())];
        simulate_game(hidden, turns_taken, running_all);
    }

    return 0;
}

// simulate_game:  run a single round of AYTO on hidden vector
void simulate_game(const string &hidden, map<string, int> &turns_taken,
                   bool running_all)
{
    vector<string> possible_perms = ORIGPERMS;
    pair<string, int> tbguess;
    pair<string, int> pmguess;
    vector<string> guessed;
    vector<string> guessed_tb;
    int e;
    int sz = hidden.size();

    if (!running_all) {
        cout << "Running AYTO Simulator on Hidden Vector: ";
        sequence_print(hidden);
        cout << endl;
    }

    for (int turn = 1; ; ++turn) {
        // stage one: truth booth
        if (!running_all) {
            cout << "**** Round " << turn << "A ****" << endl;
            cout << "Num. Possibilities: " << possible_perms.size() << endl;
        }
        tbguess = guess_tb(possible_perms, guessed_tb, turn);
        if (!running_all) {
            cout << "Guess: ";
            sequence_print(tbguess.first);
            cout << endl;
            e = tbguess.second;
            cout << "Worst-Case Evaluation: " << e << endl;
        } else {
            e = evaluate(hidden, tbguess.first);
        }
        possible_perms = remove_perms(possible_perms, e, tbguess.first);

        // stage two: perfect matching
        if (!running_all) {
            cout << "Round " << turn << "B" << endl;
            cout << "Num. Possibilities: " << possible_perms.size() << endl;
        }
        pmguess = guess_pm(possible_perms, guessed, turn);

        if (!running_all) {
            cout << "Guess: ";
            sequence_print(pmguess.first);
            cout << endl;
            e = pmguess.second;
            cout << "Worst-Case Evaluation: " << e << endl;
        } else {
            e = evaluate(hidden, pmguess.first);
        }
        if (e == sz) {
            cout << "Found ";
            sequence_print(pmguess.first);
            cout << " in " << turn << " guesses." << endl;
            turns_taken[pmguess.first] = turn;
            break;
        }

        possible_perms = remove_perms(possible_perms, e, pmguess.first);
    }
}

// map_avg:  returns average int component of a map<string, int> type
double map_avg(const map<string, int> &mp)
{
    double sum = 0.0;

    for (map<string, int>::const_iterator it = mp.begin(); 
         it != mp.end(); ++it) {
        sum += it->second;
    }

    return sum / mp.size();
}

// picmp:  comparison function for pair<string, int> types, via int component
bool picmp(const pair<string, int> &p1, const pair<string, int> &p2)
{
    return p1.second < p2.second;
}

// nrand:  random integer in range [0, n)
int nrand(int n)
{
    srand(time(NULL));

    return rand() % n;
}

// evaluate:  number of black hits from permutation or truth booth query
int evaluate(const string &sol, const string &query)
{
    int hits = 0;

    if (sol.size() == query.size()) {
        // permutation query
        int s = sol.size();
        for (int i = 0; i < s; i++) {
            if (sol[i] == query[i])
                ++hits;
        }
    } else {
        // truth booth query
        if (sol[atoi(query.substr(0, 1).c_str())] == query[1])
            ++hits;
    }

    return hits;
}

// remove_perms:  remove solutions that are no longer possible after an eval
vector<string> remove_perms(vector<string> &perms, int eval, string &query)
{
    vector<string> new_perms;

    for (vector<string>::iterator i = perms.begin(); i != perms.end(); i++) {
        if (evaluate(*i, query) == eval) {
            new_perms.push_back(*i);
        }
    }

    return new_perms;
}

// guess_tb:  guesses best pair (pos, val) to go to the truth booth
pair<string, int> guess_tb(vector<string> &possible_perms,
                           vector<string> &guessed_tb, int turn)
{
    static const string digits = "0123456789";
    int n = possible_perms[0].size();
    pair<string, int> next_guess;

    if (turn == 1) {
        next_guess.first = "00";
        next_guess.second = 0;
    } else if (possible_perms.size() == 1) {
        next_guess.first = "0" + possible_perms[0].substr(0, 1);
        next_guess.second = 1;
    } else {
        map<string, double> pair_to_count;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                pair_to_count[digits.substr(i, 1) + digits.substr(j, 1)] = 0;
            }
        }

        // count up the occurrences of each pair in the possible perms
        for (vector<string>::iterator p = possible_perms.begin();
             p != possible_perms.end(); p++) {
            int len = possible_perms[0].size();
            for (int i = 0; i < len; i++) {
                pair_to_count[digits.substr(i, 1) + (*p).substr(i, 1)] += 1;
            }
        }

        double best_dist = 1;
        int perm_cnt = possible_perms.size();
        for (map<string, double>::iterator i = pair_to_count.begin();
             i != pair_to_count.end(); i++) {
            if (find(guessed_tb.begin(), guessed_tb.end(), i->first)
                == guessed_tb.end()) {
                // hasn't been guessed yet
                if (abs(i->second/perm_cnt - .5) < best_dist) {
                    next_guess.first = i->first;
                    best_dist = abs(i->second/perm_cnt - .5);
                    if (i->second / perm_cnt < 0.5) // occurs in < half perms
                        next_guess.second = 0;
                    else                            // occurs in >= half perms
                        next_guess.second = 1;
                }
            }
        }
    }

    guessed_tb.push_back(next_guess.first);

    return next_guess;
}

// guess_pm:  guess a full permutation using minimax
pair<string, int> guess_pm(vector<string> &possible_perms,
                           vector<string> &guessed, int turn)
{
    static const string digits = "0123456789";
    pair<string, int> next_guess;
    vector<vector<string> > chunks;
    int sz = possible_perms[0].size();

    // on first turn, we guess "0, 1, ..., n-1" if truth booth was correct
    // or "1, 0, ..., n-1" if truth booth was incorrect
    if (turn == 1) {
        int fact, i;
        for (i = 2, fact = 1; i <= sz; fact *= i++)
            ;
        if (possible_perms.size() == fact) {
            next_guess.first = digits.substr(0, sz);
            next_guess.second = 1;
        } else {
            next_guess.first = "10" + digits.substr(2, sz - 2);
            next_guess.second = 1;
        }
    } else if (possible_perms.size() == 1) {
        next_guess.first = possible_perms[0];
        next_guess.second = possible_perms[0].size();
    } else {
        // run multi-threaded minimax to get next guess
        pair<string, int> candidates[NUM_CHUNKS];
        vector<thread> jobs;
        make_chunks(ORIGPERMS, chunks, NUM_CHUNKS);
        struct args **args = create_args(possible_perms, candidates, chunks[0], 0);

        for (int j = 0; j < NUM_CHUNKS; j++) {
            args[j]->chunk = &(chunks[j]);
            args[j]->thread_id = j;
            jobs.push_back(thread(get_score, args[j]));
        }
        for (int j = 0; j < NUM_CHUNKS; j++) {
            jobs[j].join();
        }

        next_guess.first = min_candidate(candidates, NUM_CHUNKS);
        next_guess.second = wc_response(next_guess.first, possible_perms);

        for (int j = 0; j < NUM_CHUNKS; j++)
            free(args[j]);
        free(args);
    }

    guessed.push_back(next_guess.first);

    return next_guess;
}

struct args **create_args(vector<string> &perms, pair<string, int> *cd, vector<string> &chunk, int thread_id)
{
    struct args **args = (struct args **) malloc(sizeof(struct args*)*NUM_CHUNKS);
    assert(args);
    for (int i = 0; i < NUM_CHUNKS; i++) {
        args[i] = (struct args *) malloc(sizeof(struct args));
        assert(args[i]);
        args[i]->perms = &perms;
        args[i]->cd = cd;
    }

    return args;
}

// make_chunks:  return pointers to n (nearly) equally sized vectors
//                from the original vector
void make_chunks(vector<string> &orig, vector<vector<string> > &chunks, int n)
{
    int sz = orig.size();
    int chunk_sz = sz / n;
    int n_with_extra = sz % n;
    vector<string>::iterator b = orig.begin();
    vector<string>::iterator e;

    for (int i = 0; i < n; i++) {
        int m = chunk_sz;    // size of this chunk
        if (n_with_extra) {
            ++m;
            --n_with_extra;
        }
        e = b + m;
        vector<string> subvec(b, e);
        chunks.push_back(subvec);
        b = e;
    }
}

// min_candidate:  string with min int from array of pair<string, ints>
string min_candidate(pair<string, int> *candidates, int n)
{
    int i, minsofar;
    string minstring;

    minstring = candidates[0].first;
    minsofar = candidates[0].second;
    for (i = 1; i < n; ++i) {
        if (candidates[i].second < minsofar) {
            minsofar = candidates[i].second;
            minstring = candidates[i].first;
        }
    }

    return minstring;
}

// get_score:  find the maximum number of remaining solutions over all
//             possible responses to the query s
//             this version takes a chunk and finds the guess with lowest score
//             from that chunk
void get_score(struct args *args)
{
    // parse the args struct
    vector<string> &chunk = *(args->chunk);
    vector<string> &perms = *(args->perms);
    pair<string, int> *cd = args->cd;
    int thread_id = args->thread_id;

    typedef vector<string>::const_iterator vec_iter;
    int sz = perms[0].size();

    pair<string, int> best_guess;
    best_guess.second = perms.size();
    int wc_num_remaining;
    for (vec_iter s = chunk.begin(); s != chunk.end(); ++s) {
        vector<int> matches(sz + 1, 0);
        for (vec_iter p = perms.begin(); p != perms.end(); ++p) {
            ++matches[evaluate(*s, *p)];
        }
        wc_num_remaining = *max_element(matches.begin(), matches.end());
        if (wc_num_remaining < best_guess.second) {
            best_guess.first = *s;
            best_guess.second = wc_num_remaining;
        }
    }

    cd[thread_id] = best_guess;

    return;
}

// wc_response:  the response to guess that eliminates the least solutions
int wc_response(string &guess, vector<string> &perms)
{
    map<int, int> matches_eval;

    for (vector<string>::iterator it = perms.begin(); it!=perms.end(); ++it) {
        ++matches_eval[evaluate(guess, *it)];
    }

    return max_element(matches_eval.begin(), matches_eval.end(), prcmp)->first;
}

// prcmp:  comparison function for pair<int, int> types in map
bool prcmp(pair<int, int> x, pair<int, int> y)
{
    return x.second < y.second;
}

void sequence_print(const string s)
{
    for (string::const_iterator i = s.begin(); i != s.end(); i++) {
        if (i == s.begin())
            cout << "(";
        cout << *i;
        if (i != s.end() - 1)
            cout << ", ";
        else
            cout << ")";
    }
}

我已将其移至“你是那个人”吗?在GitHub上具有更新的,更快的代码。
克里斯·谢特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.