돌을 강에 던질 한 쌍의 스파이를 만드십시오


20

최근에 새로 출시 된 Puzzling.SE 에서 스파이가 강에 돌을 던지는 데 실제로 문제가있었습니다.

두 명의 스파이는 적에게 눈에 띄지 않고 두 개의 비밀 번호 (스파이 당 하나의 번호)를 서로 전달해야합니다. 그들은 26 개의 구별 할 수없는 돌을 사전에 사용하여이를 수행하는 방법에 동의했다.

그들은 26 개의 돌 더미가있는 강에서 만난다. 첫 번째 스파이부터 시작하여 그들은 돌 그룹을 강에 던졌습니다. 첫 번째 스파이는 몇 개의 돌을 던지고 두 번째는 돌을 던졌습니다.

모든 스파이는 돌이 모두 사라질 때까지 자신의 차례에 적어도 하나의 돌을 던져야합니다.

그들은 돌이 더 이상 없을 때 모든 던지기를 관찰하고 분기합니다. 그들은 항상 침묵을 지킨다. 그리고 매 턴마다 던져진 돌의 수를 제외하고는 정보가 교환되지 않는다.

숫자가 1에서 M까지 일 수 있다면 어떻게 숫자를 성공적으로 교환 할 수 있습니까?

당신의 작업은 프로그램의 한 쌍을 구축하는 것입니다, spy1그리고 spy2가장 높은이 문제를 해결할 수있다, M.

귀하의 프로그램은 각각 입력으로 1선택한 번호를받습니다 M. 그런 다음 spy1강에 던지는 돌 수를 나타내는 숫자를 출력합니다.이 숫자는에 입력 할 spy2숫자도 출력하고 숫자가 spy1더해질 때까지 계속됩니다 26. 던지기가 끝나면 각 프로그램은 다른 프로그램이 가지고 있다고 생각하는 숫자를 출력합니다.이 숫자는 실제로 다른 프로그램에 입력 된 숫자와 일치해야합니다.

여러분의 프로그램은 숫자의 모든 가능한 순서쌍을 위해 일해야 (i, j)모두 ij다를 수 있습니다 1M.

가장 큰 프로그램 M은 우승자가 될 것이며, 첫 번째 답변은 동점입니다. 또한, 첫 번째로 효과가 입증 된 솔루션에 +100의 평판 현상금을 M >= 2286, 그리고 첫 번째로 효과가 입증 된 솔루션에 +300을 부여합니다 M >= 2535.


해결책은 각 (i, j)에 대해 일련의 장애를 생성하는 알고리즘 또는 프로그램을 의미합니까?
klm123

하나의 프로그램이 아니라 두 개의 프로그램입니다. 문제와 같이 독립적으로 의사 소통해야합니다.
Joe Z.

3
프로그램은 그들의 의사 결정 트리를 공유해야하므로, 어떤 스파이인지를 주장하기 위해 하나의 프로그램으로 만들 수 있습니까?
피터 테일러

각 스파이가 독립적으로 통신하고 그들 사이에 추가 정보가 교환되는 것을 보장 할 수있는 한.
Joe Z.

독립적으로 나는 2535 가이 문제에 대한 정보 이론적 인 최대 값임을 확인했습니다. 나는 어떤 프로그램도 더 잘 할 수 없다고 강력하게 믿는다.
nneonneo 2016 년

답변:


8

C #, M = 2535

이렇게하면이 콘테스트를 유발 한 스레드에서 수학적으로 설명한 시스템이 구현됩니다. 300 담당자 보너스를 청구합니다. 명령 줄 인수없이 또는 명령 줄 인수로 프로그램을 실행하면 프로그램이 자체 테스트됩니다 --test. 스파이 1의 경우로 실행하고 --spy1스파이 2의 경우로 실행 --spy2합니다. 각각의 경우 stdin에서 통신 해야하는 숫자를 취한 다음 stdin 및 stdout을 통해 던지기를 수행합니다.

* 실제로, 결정 트리를 생성하는 데 몇 분에서 1 초 미만으로 큰 차이를 만드는 최적화를 찾았습니다. 그것이 생성하는 나무는 기본적으로 동일하지만, 나는 여전히 그 증거를 연구하고 있습니다. 다른 곳에서 설명한 시스템을 직접 구현 하려면 추가 로깅 과 더 나은 스레드 간 통신 을 백 포트하려는 경우에도 개정판 2를 참조하십시오 .MainTestSpyIO

당신은 분, 변경 이내에 완료 테스트 케이스하려는 경우 N16M에를 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컴파일 (데비안 기반 시스템에서, 그것은에서의 mono-devel패키지) 및 mono(실행 mono-runtime패키지). 그런 다음 주술은

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

기타


2
C #입니까? 나는 리눅스에서 어떻게 실행하는지 모른다.
Joe Z.

이번에는 내가 뭔가 잘못하고 있다고 생각했습니다. 결과적으로 의사 결정 트리를 작성하는 데 30 분이 소요됩니다 ... 기록상 이것은 Fedora 20에서 작동합니다 yum install mono-core. 2. dmcs Puzzle625.cs3.mono Puzzle625.exe --test
데니스

@Dennis, Mono의 JIT는 Microsoft만큼 좋지 않다고 생각합니다. 최적화에 대한 아이디어가 있지만 테스트를 완료하지 않았습니다.
피터 테일러

Fedora의 리포지토리는 2 년이 지난 2.10.8 버전을 제공합니다. 최신 버전이 더 빠를 수도 있습니다. 궁금합니다 : Microsoft에 시간이 얼마나 걸립니까?
Dennis

2
30 분 ~ 39 마이크로 초 그것이 내가 최적화라고 부르는 것입니다!
Dennis

1

파이썬 테스터 프로그램

구현이 작동하는지 확인할 수있는 테스트 프로그램이 있으면 도움이 될 것입니다. 아래의 두 스크립트는 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)

프로토콜 : 명령 줄에 지정된 두 개의 스파이 프로그램이 실행됩니다. stdin / stdout을 통해서만 상호 작용할 것으로 예상됩니다. 각 프로그램은 할당 된 번호를 첫 번째 입력 줄로받습니다. 각 턴에서, 스파이 1은 던질 돌의 수를 출력하고, 스파이 2는 stdin (스파이 1의 던짐을 나타냄)에서 숫자를 읽은 다음 반복합니다 (위치가 반전 됨). 두 스파이가 26 개의 돌이 던져 졌다고 판단하면 다른 스파이의 수에 대한 추측을 멈추고 출력합니다.

spy1과 호환되는 세션 예 (Spy >에 대한 입력을 나타냄)

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

당신이 매우 큰 M을 선택하고 실행하는 데 시간이 너무 오래 걸리는 경우 전환 할 수 있습니다 test(에 대한 testrand(무작위 검사를 실행하는 마지막 줄에. 후자의 경우 적어도 수천 번의 시련을 위해 프로그램을 실행하여 신뢰를 쌓으십시오.

spy.pyM = 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

자바, M = 2535

좋아, 여기 내 구현이 있습니다. 각 단계에서 한 명의 스파이가 움직입니다. 각각의 가능한 움직임은 코드의 범위를 나타냅니다. 스파이는 자신의 비밀 코드와 일치하는 움직임을 선택합니다. 그들이 더 많은 돌을 던질 때 가능한 스파이의 범위는 결국 두 스파이 모두가 한 행동에 따라 하나의 코드 만 남을 때까지 줄어 듭니다.

비밀 코드를 복구하기 위해 모든 이동을 재생하고 해당 코드 범위를 계산할 수 있습니다. 결국 각 스파이에 대해 하나의 코드, 즉 그가 전송하고자하는 비밀 코드 만 남습니다.

불행하게도, 알고리즘은 수십만 개의 정수가있는 사전 계산 된 큰 테이블에 의존합니다. 이 방법은 8-10 개 이상의 돌로 정신적으로 적용될 수 없었습니다.

첫 번째 파일은 스파이의 알고리즘을 구현합니다. 정적 부분 codeCount은 나중에 각 이동을 계산하는 데 사용되는 테이블을 미리 계산합니다 . 두 번째 부분은 던질 돌 수를 선택하는 절차와 비밀 코드를 재구성하는 데 도움이되는 동작을 재생하는 두 가지 절차를 구현합니다.

두 번째 파일은 Spy 클래스를 광범위하게 테스트합니다. 이 방법 simulate은 프로세스를 시뮬레이션합니다. Spy 클래스를 사용하여 비밀 코드에서 일련의 throw를 생성 한 다음 시퀀스에서 코드를 재구성합니다.

스파이

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]

나는이 두 스파이를 별도의 프로세스로 실행하고 range필드에 대한 액세스를 공유하지 않고 던지기를 의사 소통 할 수있는 방법이 없으면 사양을 충족시키지 않는다고 생각 합니다. 그러나 테이블을 계산하는 방법에 매우 흥미가 있습니다. 정확성의 증거가 있습니까? 그리고 문제를 논의하고 그 해결책을 계산하는 논문을 공동으로 작성하고 싶습니까?
피터 테일러

다른 스파이의 범위는 "재생"방법으로 계산되므로 과거 이동의 함수입니다. 나는 그것이 옳다고 믿는다. 내가 계산하는 테이블은 Tk를 설정 한 것과 동일합니다. 테이블 교환 x와 y를 바꾸면 합계는 노드에서 가능한 모든 하위의 합계입니다. 나는 그것을 22 개의 돌까지 시험했다는 것을 제외하고는 그것을 정확하게 증명하지 못했다. puzzling.stackexchange에 대한 적절한 답변을 작성하려고했지만 확실하고 확실한 방법으로 설명하지 못했습니다. 그리고 대부분, 그것은 당신이 이미 한 일입니다.
Florian F

승인. 아마도 이번 주에는 시간이 없을 것입니다.하지만 덜 바쁠 때 생성 방법이 내 테이블과 동일한 테이블을 생성한다는 증거를 찾으려고 노력할 것입니다. 이미 작성했습니다.
Peter Taylor

실제로 그것은 매우 간단합니다. 제 계산 방법과 동등한 것은 두 파티션의 다중 집합 조합의 켤레의 켤레가 켤레의 점별 합과 같다는 것입니다.
Peter Taylor

(내 머리를 때리며) 물론! 내가 왜 그렇게 일찍 생각하지 않았습니까? :-)
Florian F

0

ksh / zsh, M = 126

이 간단한 시스템에서 각 스파이는 이진 숫자를 다른 스파이에게 던집니다. 각각의 던지기에서 첫 번째 돌은 무시되고 다음 돌은 각각 비트 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는 나가고, 175는 나가고, 176-182는 나가고, 183은 나가고, 184 ~ 186이 입력되고 187이 종료됩니다.

로 자동 스왑을 ksh spy.sh 125 126실행하고 ksh spy.sh spy1 125및로 개별 스파이를 실행하십시오 ksh 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.