川に石を投げるスパイのペアを構築します


20

最近、新しくリリースされたPuzzling.SEでスパイが川に石を投げ入れるという問題がありましたが実際には非常に困難でした。

2人のスパイは、敵に気付かれずに2つの秘密の番号(スパイごとに1つの番号)を渡す必要があります。彼らは前もってわずか26個の区別できない石を使用してこれを行う方法に同意しました。

彼らは26の石の山がある川で会います。最初のスパイから始めて、彼らは交互に石のグループを川に投げます:最初のスパイはいくつかの石を投げ、次に2番目のもの、そして最初のものを再び投げます...

すべての石がなくなるまで、各スパイは自分のターンに少なくとも1つの石を投げなければなりません。

彼らはすべての投球を観察し、石がなくなると発散します。彼らは常に沈黙を保ち、各ターンに投げられる石の数を除いて情報は交換されません。

番号が1からMまでの場合、どうすれば番号を正常に交換できますか?

あなたの仕事はプログラムのペアを構築することです、spy1そしてspy2可能な限り最高のために、この問題を解決することができ、M

あなたのプログラムはそれぞれ、入力として1選択さMれた番号を受け取ります。次に、spy1川に投げる石の数を表す数値を出力します。これは、入力されるspy2数値も出力しspy1、数値出力が合計されるまで続き26ます。スローが終了すると、各プログラムは、他のプログラムが持っていると信じている番号を出力します。これは、他のプログラムに実際に入力された番号と一致する必要があります。

プログラムは(i, j)、両方のiおよびjから1に変化する可能性のあるすべての可能な番号のペアに対して機能する必要がありますM

最大の規模で機能するプログラムがM勝者となり、最初の回答が同点になります。さらに、私は、機能することが証明された最初のソリューションに対して+100の評価賞金を、機能することが証明さM >= 2286れた最初のソリューションに対して+300を授与しM >= 2535ます。


解決策とは、(i、j)ごとに分散のセットを生成するアルゴリズムまたはプログラムを意味しますか?
klm123 14年

1つのプログラムではなく、2つ。あなたの問題のように、彼らは独立して通信しなければなりません。
ジョーZ.

3
プログラムはデシジョンツリーを共有する必要があるため、どのスパイであるかを指定する引数を取る1つのプログラムにすることはできますか?
ピーターテイラー

各スパイが独立して通信し、それらの間で追加の情報が交換されないことを保証できる限り。
ジョーZ.

独立して、2535がこの問題の情報理論上の最大値であることを確認しました。私は今、どのプログラムもこれ以上良いことはできないと強く信じています。
nneonneo

回答:


8

C#、M = 2535

これは、このコンテストを引き起こしたスレッドで数学的に説明したシステムを実装します。300 repボーナスを請求します。プログラム--testは、コマンドライン引数なしで、またはコマンドライン引数として実行すると、自己テストします。スパイ1の場合はで実行し--spy1、スパイ2の場合はで実行し--spy2ます。いずれの場合も、stdinから通信する必要がある番号を取得し、stdinとstdoutを介してスローを実行します。

*実際、私は、大きな違いを生む最適化を見つけました(数分から決定木を生成するまで、1秒未満まで)。それが生成するツリーは基本的に同じですが、私はまだその証拠に取り組んでいます。他の場所で説明したシステムの直接実装が必要な場合は、リビジョン2を使用すると、より余分のロギングバックポートしたいかもしれませんが、Mainそしてより良いスレッド間の途切れをTestSpyIO

あなたが分未満で完了したテストケースをしたい場合は、変更Nする16Mします87

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace CodeGolf
{
    internal class Puzzle625
    {
        public static void Main(string[] args)
        {
            const int N = 26;
            const int M = 2535;

            var root = BuildDecisionTree(N);

            if (args.Length == 0 || args[0] == "--test")
            {
                DateTime startUtc = DateTime.UtcNow;
                Console.WriteLine("Built decision tree in {0}", DateTime.UtcNow - startUtc);
                startUtc = DateTime.UtcNow;

                int ok = 0;
                int fail = 0;
                for (int i = 1; i <= M; i++)
                {
                    for (int j = 1; j <= M; j++)
                    {
                        if (Test(i, j, root)) ok++;
                        else fail++;
                    }
                    double projectedTimeMillis = (DateTime.UtcNow - startUtc).TotalMilliseconds * M / i;
                    Console.WriteLine("Interim result: ok = {0}, fail = {1}, projected test time {2}", ok, fail, TimeSpan.FromMilliseconds(projectedTimeMillis));
                }
                Console.WriteLine("All tested: ok = {0}, fail = {1}, in {2}", ok, fail, DateTime.UtcNow - startUtc);
                Console.ReadKey();
            }
            else if (args[0] == "--spy1")
            {
                new Spy(new ConsoleIO(), root, true).Run();
            }
            else if (args[0] == "--spy2")
            {
                new Spy(new ConsoleIO(), root, false).Run();
            }
            else
            {
                Console.WriteLine("Usage: Puzzle625.exe [--test|--spy1|--spy2]");
            }
        }

        private static bool Test(int i, int j, Node root)
        {
            TestSpyIO io1 = new TestSpyIO("Spy 1");
            TestSpyIO io2 = new TestSpyIO("Spy 2");
            io1.Partner = io2;
            io2.Partner = io1;

            // HACK! Prime the input
            io2.Output(i);
            io1.Output(j);

            Spy spy1 = new Spy(io1, root, true);
            Spy spy2 = new Spy(io2, root, false);

            Thread th1 = new Thread(spy1.Run);
            Thread th2 = new Thread(spy2.Run);
            th1.Start();
            th2.Start();

            th1.Join();
            th2.Join();

            // Check buffer contents. Spy 2 should output spy 1's value, so it's lurking in io1, and vice versa.
            return io1.Input() == i && io2.Input() == j;
        }

        private static Node BuildDecisionTree(int numStones)
        {
            NodeValue[] trees = new NodeValue[] { NodeValue.Trivial };
            for (int k = 2; k <= numStones; k++)
            {
                int[] prev = trees.Select(nv => nv.Y).ToArray();
                List<int> row = new List<int>(prev);
                int cap = prev.Length;
                for (int i = 1; i <= prev[0]; i++)
                {
                    while (prev[cap - 1] < i) cap--;
                    row.Add(cap);
                }

                int[] next = row.OrderByDescending(x => x).ToArray();
                NodeValue[] nextTrees = new NodeValue[next.Length];
                nextTrees[0] = trees.Last().Reverse();
                for (int i = 1; i < next.Length; i++)
                {
                    int cp = next[i] - 1;
                    nextTrees[i] = trees[cp].Combine(trees[i - prev[cp]]);
                }

                trees = nextTrees;
            }

            NodeValue best = trees.MaxElement(v => Math.Min(v.X, v.Y));
            return BuildDecisionTree(numStones, best, new Dictionary<Pair<int, NodeValue>, Node>());
        }

        private static Node BuildDecisionTree(int numStones, NodeValue val, IDictionary<Pair<int, NodeValue>, Node> cache)
        {
            // Base cases
            // NB We might get passed val null with 0 stones, so we hack around that
            if (numStones == 0) return new Node(NodeValue.Trivial, new Node[0]);

            // Cache
            Pair<int, NodeValue> key = new Pair<int, NodeValue>(numStones, val);
            Node node;
            if (cache.TryGetValue(key, out node)) return node;

            // The pair-of-nodes construction is based on a bijection between
            //     $\prod_{i<k} T_i \cup \{(\infty, 0)\}$
            // and
            //     $(T_{k-1} \cup \{(\infty, 0)\}) \times \prod_{i<k-1} T_i \cup \{(\infty, 0)\}$

            // val.Left represents the element of $T_{k-1} \cup \{(\infty, 0)\}$ (using null for the $(\infty, 0)$)
            // and val.Right represents $\prod_{i<k-1} T_i \cup \{(\infty, 0)\}$ by bijection with $T_{k-1} \cup \{(\infty, 0)\}$.
            // so val.Right.Left represents the element of $T_{k-2}$ and so on.
            // The element of $T_{k-i}$ corresponds to throwing $i$ stones.
            Node[] children = new Node[numStones];
            NodeValue current = val;
            for (int i = 0; i < numStones && current != null; i++)
            {
                children[i] = BuildDecisionTree(numStones - (i + 1), current.Left, cache);
                current = current.Right;
            }
            node = new Node(val, children);

            // Cache
            cache[key] = node;
            return node;
        }

        class Pair<TFirst, TSecond>
        {
            public readonly TFirst X;
            public readonly TSecond Y;

            public Pair(TFirst x, TSecond y)
            {
                this.X = x;
                this.Y = y;
            }

            public override string ToString()
            {
                return string.Format("({0}, {1})", X, Y);
            }

            public override bool Equals(object obj)
            {
                Pair<TFirst, TSecond> other = obj as Pair<TFirst, TSecond>;
                return other != null && object.Equals(other.X, this.X) && object.Equals(other.Y, this.Y);
            }

            public override int GetHashCode()
            {
                return X.GetHashCode() + 37 * Y.GetHashCode();
            }
        }

        class NodeValue : Pair<int, int>
        {
            public readonly NodeValue Left;
            public readonly NodeValue Right;

            public static NodeValue Trivial = new NodeValue(1, 1, null, null);

            private NodeValue(int x, int y, NodeValue left, NodeValue right) : base(x, y)
            {
                this.Left = left;
                this.Right = right;
            }

            public NodeValue Reverse()
            {
                return new NodeValue(Y, X, this, null);
            }

            public NodeValue Combine(NodeValue other)
            {
                return new NodeValue(other.X + Y, Math.Min(other.Y, X), this, other);
            }
        }

        class Node
        {
            public readonly NodeValue Value;
            private readonly Node[] _Children;

            public Node this[int n]
            {
                get { return _Children[n]; }
            }

            public int RemainingStones
            {
                get { return _Children.Length; }
            }

            public Node(NodeValue value, IEnumerable<Node> children)
            {
                this.Value = value;
                this._Children = children.ToArray();
            }
        }

        interface SpyIO
        {
            int Input();
            void Output(int i);
        }

        // TODO The inter-thread communication here can almost certainly be much better
        class TestSpyIO : SpyIO
        {
            private object _Lock = new object();
            private int? _Buffer;
            public TestSpyIO Partner;
            public readonly string Name;

            internal TestSpyIO(string name)
            {
                this.Name = name;
            }

            public int Input()
            {
                lock (_Lock)
                {
                    while (!_Buffer.HasValue) Monitor.Wait(_Lock);

                    int rv = _Buffer.Value;
                    _Buffer = null;
                    Monitor.PulseAll(_Lock);
                    return rv;
                }
            }

            public void Output(int i)
            {
                lock (Partner._Lock)
                {
                    while (Partner._Buffer.HasValue) Monitor.Wait(Partner._Lock);
                    Partner._Buffer = i;
                    Monitor.PulseAll(Partner._Lock);
                }
            }
        }

        class ConsoleIO : SpyIO
        {
            public int Input()
            {
                return Convert.ToInt32(Console.ReadLine());
            }

            public void Output(int i)
            {
                Console.WriteLine("{0}", i);
            }
        }

        class Spy
        {
            private readonly SpyIO _IO;
            private Node _Node;
            private bool _MyTurn;

            internal Spy(SpyIO io, Node root, bool isSpy1)
            {
                this._IO = io;
                this._Node = root;
                this._MyTurn = isSpy1;
            }

            internal void Run()
            {
                int myValue = _IO.Input() - 1;
                int hisValue = 1;

                bool myTurn = _MyTurn;
                Node n = _Node;
                while (n.RemainingStones > 0)
                {
                    if (myTurn)
                    {
                        if (myValue >= n.Value.X) throw new Exception("Internal error");
                        for (int i = 0; i < n.RemainingStones; i++)
                        {
                            // n[i] allows me to represent n[i].Y values: 0 to n[i].Y - 1
                            if (myValue < n[i].Value.Y)
                            {
                                _IO.Output(i + 1);
                                n = n[i];
                                break;
                            }
                            else myValue -= n[i].Value.Y;
                        }
                    }
                    else
                    {
                        int thrown = _IO.Input();
                        for (int i = 0; i < thrown - 1; i++)
                        {
                            hisValue += n[i].Value.Y;
                        }
                        n = n[thrown - 1];
                    }

                    myTurn = !myTurn;
                }

                _IO.Output(hisValue);
            }
        }
    }

    static class LinqExt
    {
        // I'm not sure why this isn't built into Linq.
        public static TElement MaxElement<TElement>(this IEnumerable<TElement> e, Func<TElement, int> f)
        {
            int bestValue = int.MinValue;
            TElement best = default(TElement);
            foreach (var elt in e)
            {
                int value = f(elt);
                if (value > bestValue)
                {
                    bestValue = value;
                    best = elt;
                }
            }
            return best;
        }
    }
}

Linuxユーザー向けの手順

あなたは必要がありますmono-csc(それは中ですが、Debianベースのシステム上でコンパイルするmono-develパッケージ)とmono(実行するためにmono-runtimeパッケージを)。それから呪文は

mono-csc -out:codegolf31673.exe codegolf31673.cs
mono codegolf31673.exe --test


2
それはC#ですか?Linuxでそれを実行する方法がわかりません。
ジョーZ.

この間ずっと、私は何か間違ったことをしていると思った。結局のところ、意思決定ツリーの構築には30分しかかかりません...記録のために、これはFedora 20:1で機能しますyum install mono-core(ルートとして)。2. dmcs Puzzle625.cs3.mono Puzzle625.exe --test
デニス

@ Dennis、MonoのJITはMicrosoftのJITほど優れていないと思います。最適化のアイデアはいくつかありますが、テストはまだ完了していません。
ピーターテイラー

Fedoraのリポジトリは2年以上前のバージョン2.10.8を提供します。新しいバージョンの方が速いかもしれません。私は好奇心が強いです:マイクロソフトでどれくらいかかりますか?
デニス

2
30分から39マイクロ秒まで。それは私が最適化と呼んでいるものです!
デニス14年

1

Pythonテスタープログラム

実装が機能していることを確認できるテストプログラムがあると便利だと思います。以下の両方のスクリプトは、Python 2またはPython 3で動作します。

テスタープログラム(tester.py):

import sys
import shlex
from subprocess import Popen, PIPE

def writen(p, n):
    p.stdin.write(str(n)+'\n')
    p.stdin.flush()

def readn(p):
    return int(p.stdout.readline().strip())

MAXSTONES = 26

def test_one(spy1cmd, spy2cmd, n1, n2):
    p1 = Popen(spy1cmd, stdout=PIPE, stdin=PIPE, universal_newlines=True)
    p2 = Popen(spy2cmd, stdout=PIPE, stdin=PIPE, universal_newlines=True)

    nstones = MAXSTONES

    writen(p1, n1)
    writen(p2, n2)

    p1turn = True
    while nstones > 0:
        if p1turn:
            s = readn(p1)
            writen(p2, s)
        else:
            s = readn(p2)
            writen(p1, s)
        if s <= 0 or s > nstones:
            print("Spy %d output an illegal number of stones: %d" % ([2,1][p1turn], s))
            return False
        p1turn = not p1turn
        nstones -= s

    n1guess = readn(p2)
    n2guess = readn(p1)

    if n1guess != n1:
        print("Spy 2 output wrong answer: expected %d, got %d" % (n1, n1guess))
        return False
    elif n2guess != n2:
        print("Spy 1 output wrong answer: expected %d, got %d" % (n2, n2guess))
        return False

    p1.kill()
    p2.kill()

    return True

def testrand(spy1, spy2, M):
    import random
    spy1cmd = shlex.split(spy1)
    spy2cmd = shlex.split(spy2)

    n = 0
    while 1:
        i = random.randrange(1, M+1)
        j = random.randrange(1, M+1)
        test_one(spy1cmd, spy2cmd, i, j)
        n += 1
        if n % 100 == 0:
            print("Ran %d tests" % n)

def test(spy1, spy2, M):
    spy1cmd = shlex.split(spy1)
    spy2cmd = shlex.split(spy2)
    for i in range(1, M+1):
        print("Testing %d..." % i)
        for j in range(1, M+1):
            if not test_one(spy1cmd, spy2cmd, i, j):
                print("Spies failed the test.")
                return
    print("Spies passed the test.")

if __name__ == '__main__':
    if len(sys.argv) != 4:
        print("Usage: %s <M> <spy1> <spy2>: test programs <spy1> and <spy2> with limit M" % sys.argv[0])
        exit()

    M = int(sys.argv[1])
    test(sys.argv[2], sys.argv[3], M)

プロトコル:コマンドラインで指定された2つのスパイプログラムが実行されます。それらは、stdin / stdoutを介してのみ相互作用することが期待されています。各プログラムは、割り当てられた番号を入力の最初の行として受け取ります。各ターンで、スパイ1は投球する石の数を出力し、スパイ2は標準入力(スパイ1の投球を表す)から数字を読み取り、それらを繰り返します(位置を逆にして)。いずれかのスパイが26個の石が投げられたと判断すると、それらは停止し、他のスパイの数に対する推測を出力します。

互換性のあるspy1とのセッションの例(>spyへの入力を示します)

> 42
7
> 5
6
> 3
5
27
<program quits>

非常に大きなMを選択し、実行に時間がかかりすぎる場合は、最後の行に切り替えtest(testrand(ランダムテストを実行できます。後者の場合、自信を高めるために、少なくとも数千回の試行のためにプログラムを実行したままにします。

プログラム例(spy.py)、M = 42の場合:

import sys

# Carry out the simple strategy for M=42

def writen(n):
    sys.stdout.write(str(n)+"\n")
    sys.stdout.flush()

def readn():
    return int(sys.stdin.readline().strip())

def spy1(n):
    m1,m2 = divmod(n-1, 6)
    writen(m1+1)
    o1 = readn() # read spy2's number

    writen(m2+1)
    o2 = readn()

    rest = 26 - (m1+m2+o1+o2+2)
    if rest > 0:
        writen(rest)
    writen((o1-1)*6 + (o2-1) + 1)

def spy2(n):
    m1,m2 = divmod(n-1, 6)
    o1 = readn() # read spy1's number
    writen(m1+1)

    o2 = readn()
    writen(m2+1)

    rest = 26 - (m1+m2+o1+o2+2)
    if rest > 0:
        readn()

    writen((o1-1)*6 + (o2-1) + 1)

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage: %s [spy1|spy2]" % (sys.argv[0]))
        exit()

    n = int(input())
    if sys.argv[1] == 'spy1':
        spy1(n)
    elif sys.argv[1] == 'spy2':
        spy2(n)
    else:
        raise Exception("Must give spy1 or spy2 as an argument.")

使用例:

python tester.py 42 'python spy.py spy1' 'python spy.py spy2'

1

Java、M = 2535

OK、これが私の実装です。各ステップで1人のスパイが動きます。それぞれの可能な動きは、コードの範囲を表します。スパイは自分の秘密のコードに一致する動きを選択します。彼らがより多くの石を投げると、可能なコードの範囲は、最後に、両方のスパイに対して、彼らが行った動きに応じて1つのコードのみが可能になるまで減少します。

秘密のコードを回復するには、すべての動きを再生して、対応するコード範囲を計算します。最後に、スパイごとに残るコードは1つだけです。これは、送信したい秘密のコードです。

残念ながら、このアルゴリズムは数十万の整数を持つ大きな事前計算テーブルに依存しています。この方法は、8〜10個以上の石で精神的に適用することはできませんでした。

最初のファイルは、スパイのアルゴリズムを実装しています。静的部分は事前計算しますcodeCountは、各移動の計算に後で使用されるテーブルをます。2番目の部分は2つの手順を実行します。1つは投石する石の数を選択し、もう1つは秘密のコードを再構築するのに役立つ動きを再生します。

2番目のファイルは、Spyクラスを広範囲にわたってテストします。方法simulateはプロセスをシミュレートします。Spyクラスを使用して、シークレットコードから一連のスローを生成し、シーケンスからコードを再構築します。

Spy.java

package stackexchange;

import java.util.Arrays;

public class Spy
{
    // STATIC MEMBERS

    /** Size of code range for a number of stones left to the other and the other spy's range */
    static int[][] codeCount;

    // STATIC METHODS

    /** Transpose an array of code counts */
    public static int[] transpose(int[] counts){
        int[] transposed = new int[counts[1]+1];
        int s = 0;
        for( int i=counts.length ; i-->0 ; ){
            while( s<counts[i] ){
                transposed[++s] = i;
            }
        }
        return transposed;
    }

    /** Add two integer arrays by element.  Assume the first is longer. */
    public static int[] add(int[] a, int[] b){
        int[] sum = a.clone();
        for( int i=0 ; i<b.length ; i++ ){
            sum[i] += b[i];
        }
        return sum;
    }

    /** Compute the code range for every response */
    public static void initCodeCounts(int maxStones){
        codeCount = new int[maxStones+1][];
        codeCount[0] = new int[] {0,1};
        int[] sum = codeCount[0];
        for( int stones=1 ; stones<=maxStones ; stones++ ){
            codeCount[stones] = transpose(sum);
            sum = add(codeCount[stones], sum);
        }
    }

    /** display the code counts */
    public static void dispCodeCounts(int maxStones){
        for( int stones=1 ; stones<=maxStones ; stones++ ){
            if( stones<=8 ){
                System.out.println(stones + ": " + Arrays.toString(codeCount[stones]));
            }
        }
        for( int s=1 ; s<=maxStones ; s++ ){
            int[] row = codeCount[s];
            int best = 0;
            for( int r=1 ; r<row.length ; r++ ){
                int min = r<row[r] ? r : row[r];
                if( min>=best ){
                    best = min;
                }
            }
            System.out.println(s + ": " + row.length + " " + best);
        }
    }

    /** Find the maximum symmetrical code count M for a number of stones */
    public static int getMaxValue(int stones){
        int[] row = codeCount[stones];
        int maxValue = 0;
        for( int r=1 ; r<row.length ; r++ ){
            int min = r<row[r] ? r : row[r];
            if( min>=maxValue ){
                maxValue = min;
            }
        }
        return maxValue;
    }

    // MEMBERS

    /** low end of range, smallest code still possible */
    int min;

    /** range size, number of codes still possible */
    int range;

    /** Create a spy for a certain number of stones */
    Spy(int stones){
        min = 1;
        range = getMaxValue(stones);
    }

    /** Choose how many stones to throw */
    public int throwStones(int stonesLeft, int otherRange, int secret){
        for( int move=1 ; ; move++ ){
            // see how many codes this move covers
            int moveRange = codeCount[stonesLeft-move][otherRange];
            if( secret < this.min+moveRange ){
                // secret code is in move range
                this.range = moveRange;
                return move;
            }
            // skip to next move
            this.min += moveRange;
            this.range -= moveRange;
        }
    }

    /* Replay the state changes for a given move */
    public void replayThrow(int stonesLeft, int otherRange, int stonesThrown){
        for( int move=1 ; move<stonesThrown ; move++ ){
            int moveRange = codeCount[stonesLeft-move][otherRange];
            this.min += moveRange;
            this.range -= moveRange;
        }
        this.range = codeCount[stonesLeft-stonesThrown][otherRange];
    }
}

ThrowingStones.java

package stackexchange;

public class ThrowingStones
{
    public boolean simulation(int stones, int secret0, int secret1){

        // ENCODING

        Spy spy0 = new Spy(stones);
        Spy spy1 = new Spy(stones);

        int[] throwSequence = new int[stones+1];
        int turn = 0;
        int stonesLeft = stones;

        while( true ){
            // spy 0 throws
            if( stonesLeft==0 ) break;
            throwSequence[turn] = spy0.throwStones(stonesLeft, spy1.range, secret0);
            stonesLeft -= throwSequence[turn++];
            // spy 1 throws
            if( stonesLeft==0 ) break;
            throwSequence[turn] = spy1.throwStones(stonesLeft, spy0.range, secret1);
            stonesLeft -= throwSequence[turn++];
        }

        assert (spy0.min==secret0 && spy0.range==1 );
        assert (spy1.min==secret1 && spy1.range==1 );

//      System.out.println(Arrays.toString(throwSequence));

        // DECODING

        spy0 = new Spy(stones);
        spy1 = new Spy(stones);

        stonesLeft = stones;
        turn = 0;
        while( true ){
            // spy 0 throws
            if( throwSequence[turn]==0 ) break;
            spy0.replayThrow(stonesLeft, spy1.range, throwSequence[turn]);
            stonesLeft -= throwSequence[turn++];
            // spy 1 throws
            if( throwSequence[turn]==0 ) break;
            spy1.replayThrow(stonesLeft, spy0.range, throwSequence[turn]);
            stonesLeft -= throwSequence[turn++];
        }
        int recovered0 = spy0.min;
        int recovered1 = spy1.min;

        // check the result
        if( recovered0 != secret0 || recovered1 != secret1 ){
            System.out.println("error recovering (" + secret0 + "," + secret1 + ")"
                    + ", returns (" + recovered0 + "," + recovered1 + ")");
            return false;
        }
        return true;
    }

    /** verify all possible values */
    public void verifyAll(int stones){
        int count = 0;
        int countOK = 0;
        int maxValue = Spy.getMaxValue(stones);
        for( int a=1 ; a<=maxValue ; a++ ){
            for( int b=1 ; b<=maxValue ; b++ ){
                count++;
                if( simulation(stones, a, b) ) countOK++;
            }
        }
        System.out.println("verified: " + countOK + "/" + count);
    }

    public static void main(String[] args) {
        ThrowingStones app = new ThrowingStones();
        Spy.initCodeCounts(26);
        Spy.dispCodeCounts(26);
        app.verifyAll(20);
//      app.verifyAll(26); // never managed to complete this one...
    }

}

参考のため、事前計算されたcodeCount配列には次の値が含まれています。

1: [0, 1]
2: [0, 1, 1]
3: [0, 2, 1, 1]
4: [0, 3, 2, 1, 1, 1]
5: [0, 5, 3, 2, 2, 1, 1, 1, 1]
6: [0, 8, 5, 4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1]

これは、Peter TaylorのTkセットに直接関連しています。我々は持っています:

(x,y) in Tk  <=>  y <= codeCount[x]

2つのスパイを別々のプロセスで実行し、rangeフィールドへのアクセスを共有せずにスローを通信する方法がなければ、これが仕様を完全に満たしているとは思いません。しかし、私はあなたのテーブルの計算方法に非常に興味を持っています。正しさの証拠はありますか?そして、あなたは問題について議論し、その解決策を計算する論文で協力することに興味がありますか?
ピーターテイラー14年

他のスパイの範囲は、「リプレイ」メソッドで計算されるため、過去の動きの関数です。私はそれが正しいと信じています。私が計算するテーブルは、Tkを設定したものとまったく同じです。テーブル交換xとyを入れ替えると、合計はノードのすべての可能な子の合計になります。私はそれが正しいことを証明していませんが、最大22個の石をテストしました。puzzling.stackexchangeの適切な回答を作成しようとしましたが、明確で説得力のある方法で説明することができませんでした。そして、主に、あなたがすでにやったことです。
フロリアンF 14年

OK。今週はおそらく時間がないかもしれませんが、忙しくないときは、あなたの生成方法が私のものと同じテーブルを生成するという証拠を見つけようとします。すでに書かれています。
ピーターテイラー14年

実際、それは非常に単純です。私の計算方法との等価性は、2つのパーティションの多重集合和集合の共役がそれらの共役の点ごとの合計に等しいという補題になります。
ピーターテイラー14年

(頭を叩く)しかし、もちろん!なぜ私は以前にそれを考えなかったのですか?:-)
フロリアンF 14年

0

ksh / zsh、M = 126

この単純なシステムでは、各スパイが他のスパイに2進数を投げます。各投げでは、最初の石は無視され、次の石はそれぞれビット0、最後の石はビット1です。たとえば、20を投げる場合、スパイは4石を投げます(無視、0、2、4を追加)。 4 + 16 = 20なので、3つの石を投げます(無視、8、16を追加)。

数字のセットは連続していません。0から126は入っていますが、127は出ています。(両方のスパイが127の場合、28の石が必要ですが、26の石があります。)次に、128から158が入って、159が出て、160から174が入って、175が出て、176から182が入って、183が出て、 184〜186がイン、187がアウトなどです。

で自動スワップを実行するksh spy.sh 125 126か、またはで個々のスパイを実行ksh spy.sh spy1 125ksh spy.sh spy2 126ます。ここで、kshksh93、pdksh、またはzshを指定できます。

編集2014年6月14日:zshの一部のコプロセスの問題を修正しました。ユーザーがそれらを殺すまで、それらは永久にアイドル状態になり、終了しません。

(( stones = 26 ))

# Initialize each spy.
spy_init() {
    (( wnum = $1 ))  # my number
    (( rnum = 0 ))   # number from other spy
    (( rlog = -1 ))  # exponent from other spy
}

# Read stone count from other spy.
spy_read() {
    read count || exit
    (( stones -= count ))

    # Ignore 1 stone.
    (( count > 1 )) && {
        # Increment exponent.  Add bit to number.
        (( rlog += count - 1 ))
        (( rnum += 1 << rlog ))
    }
}

# Write stone count to other spy.
spy_write() {
    if (( wnum ))
    then
        # Find next set bit.  Prepare at least 2 stones.
        (( count = 2 ))
        until (( wnum & 1 ))
        do
            (( wnum >>= 1 ))
            (( count += 1 ))
        done

        (( wnum >>= 1 ))  # Remove this bit.
        (( stones -= count ))
        print $count      # Throw stones.
    else
        # Throw 1 stone for other spy to ignore.
        (( stones -= 1 ))
        print 1
    fi
}

# spy1 writes first.
spy1() {
    spy_init "$1"
    while (( stones ))
    do
        spy_write
        (( stones )) || break
        spy_read
    done
    print $rnum
}

# spy2 reads first.
spy2() {
    spy_init "$1"
    while (( stones ))
    do
        spy_read
        (( stones )) || break
        spy_write
    done
    print $rnum
}

(( $# == 2 )) || {
    name=${0##*/}
    print -u2 "usage: $name number1 number2"
    print -u2 "   or: $name spy[12] number"
    exit 1
}

case "$1" in
    spy1)
        spy1 "$2"
        exit;;
    spy2)
        spy2 "$2"
        exit;;
esac

(( number1 = $1 ))
(( number2 = $2 ))

if [[ -n $KSH_VERSION ]]
then
    eval 'cofork() { "$@" |& }'
elif [[ -n $ZSH_VERSION ]]
then
    # In zsh, a co-process stupidly inherits its own >&p, so it never
    # reads end of file.  Use 'coproc :' to close <&p and >&p.
    eval 'cofork() {
        coproc {
            coproc :
            "$@"
        }
    }'
fi

# Fork spies in co-processes.
[[ -n $KSH_VERSION ]] && eval 'coproc() { "$@" |& }'
cofork spy1 number1
exec 3<&p 4>&p
cofork spy2 number2
exec 5<&p 6>&p

check_stones() {
    (( stones -= count ))
    if (( stones < 0 ))
    then
        print -u2 "$1 is in trouble! " \
            "Needs $count stones, only had $((stones + count))."
        exit 1
    else
        print "$1 threw $count stones.  Pile has $stones stones."
    fi
}

# Relay stone counts while spies throw stones.
while (( stones ))
do
    # First, spy1 writes to spy2.
    read -u3 count report1 || mia spy1
    check_stones spy1
    print -u6 $count

    (( stones )) || break

    # Next, spy2 writes to spy1.
    read -u5 count report2 || mia spy2
    check_stones spy2
    print -u4 $count
done

mia() {
    print -u2 "$1 is missing in action!"
    exit 1
}

# Read numbers from spies.
read -u3 report1 || mia spy1
read -u5 report2 || mia spy2

pass=true
(( number1 != report2 )) && {
    print -u2 "FAILURE: spy1 put $number1, but spy2 got $report2."
    pass=false
}
(( number2 != report1 )) && {
    print -u2 "FAILURE: spy2 put $number2, but spy1 got $report1."
    pass=false
}

if $pass
then
    print "SUCCESS: spy1 got $report1, spy2 got $report2."
    exit 0
else
    exit 1
fi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.