Четыре шага налево: гадюки. Четыре шага направо: утес. Не умирай!

28

Введение

Предположим на мгновение, что гадюки и скалы находятся всего в двух шагах от трех.

            o
           ---
Hsss!       |
 ';;' ___  /_\  ___  _
                      |

Вы, к сожалению, пленник садистского мучителя. Вы должны сделать шаг влево или вправо каждый ход. Если вы этого не сделаете, они застрелят вас мгновенно. Вам разрешено планировать свои шаги заранее, но как только вы сделаете свой первый шаг, вы не сможете изменить свой план. (И бездельничать; тебя пристрелят.)

Внезапно, яркая идея приходит на ум ...

Ах! Я могу просто чередовать шаг вправо и влево! Шаг вправо, шаг влево, шаг вправо, шаг влево и так далее ...

Ах, ах, ах, не так быстро. Как я уже сказал, мучитель садистский. Они выбирают, делать ли вам каждый шаг, или каждый второй шаг, или каждый третий шаг, и так далее. Так что, если вы наивно выберете последовательность, RLRLRL...то они могут заставить вас сделать каждый второй шаг, который начинается с LL. Ой! Вы были укушены гадюками! Тьма налетает на тебя, а все остальное исчезает ...

На самом деле нет, ты еще не умер. Вы все еще должны придумать свой план. Подумав об этом несколько минут, вы понимаете, что обречены. Нет способа спланировать серию шагов, которые гарантируют вам выживание. Лучшее, что вы можете придумать, это RLLRLRRLLRR. 1 Одиннадцать безопасных шагов и не более. Если это двенадцатый шаг R, то Мучитель заставит вас сделать каждый шаг, а затем последние три шага отправят вас с обрыва. Если это двенадцатый шаг L, то Мучитель заставит вас сделать каждый третий шаг ( LRLL), что приведет вас прямо к выводку гадюк и их смертоносным укусам.

Вы выбираете Rв качестве двенадцатого шага, надеясь задержать вашу смерть как можно дольше. С ревом в ушах, вы удивляетесь себе ...

Что если бы у меня было три шага?


Осторожно, спойлеры!

Ты бы все равно умер. Как выясняется, независимо от того, сколько у вас шагов, будет определенная точка, где независимо от того, какой выбор вы сделаете, есть последовательность шагов, которую ваш мучитель может выбрать, чтобы убедиться, что вы встретите свою смертельную судьбу. 2 Однако, когда гадюки и обрыв находятся в трех шагах, вы можете сделать всего 1160 безопасных шагов, а когда они находятся в четырех шагах, есть как минимум 13 000 безопасных шагов! 3

Соревнование

Учитывая одно целое число n < 13000, выведите последовательность nбезопасных шагов, предполагая, что обрыв и гадюки находятся в четырех шагах.

правила

  • Может быть либо полной программой, либо функцией.
  • Ввод может быть взят через STDIN или эквивалентный, или как аргумент функции.
  • Вывод должен иметь два различных символов (которые могут быть +/-, R/L, 1/0и т.д.).
  • Любые пробелы в выводе не имеют значения.
  • Жесткое кодирование решения не допускается. Это бы упростило эту задачу.
  • Ваша программа должна (в теории) закончиться в приличном количестве времени. Как и в, n=13000может занять как месяц, но это не должно занять тысячу и более лет. То есть без грубой силы. (Ну, по крайней мере, попытайтесь избежать этого.)
  • Бонус жизни: обеспечьте серию 2000безопасных шагов. Если вы сделаете это, Мучитель будет настолько впечатлен вашим упорством, настойчивостью и предусмотрительностью, что они позволят вам жить. Это один раз. (Рассматривайте эту последовательность как двоичное число и предоставляйте десятичный эквивалент для проверки. Это предназначено для поощрения ответов, которые заканчиваются быстро, поскольку ответы могут занимать очень много времени.)
  • Оценка: байты , если вы не имеете права на бонус - умножьте на 0,75 .

Выжить!


1 Существует хорошее объяснение этой проблемы и «решения» одной из звезд Numberphile, Джеймса Грайма, на своем канале YouTube здесь: https://www.youtube.com/watch?v=pFHsrCNtJu4 .

2 Эта гипотеза 80-летней давности, известная как проблема несоответствия Эрдоса, была доказана совсем недавно Теренсом Тао. Вот очень хорошая статья в журнале Quanta об этом: https://www.quantamagazine.org/20151001-tao-erdos-discrepancy-problem/ .

3 Источник: SAT-атака на гипотезу несоответствия Эрдоса. Автор Борис Конев и Алексей Лисица. Получено здесь: http://arxiv.org/pdf/1402.2184v2.pdf .

Эльендия Старман
источник
1
Итак, если я найду решение n=13000, получат ли первые 2000 инструкций бонус? Кажется бессмысленным, так вы, вероятно, имели в виду что-то еще?
Анатолий
@anatolyg: Все решения теоретически должны быть в состоянии справиться n=13000в течение года, может быть, десяти. Ты собираешься ждать месяц n=2000? Наверное, нет. И если вы это сделаете , то вы все равно заслуживаете бонуса.
El'endia Starman

Ответы:

6

Ява, 915 * 0,75 = 686,25

import java.util.*;class E implements Comparable<E>{static
int n,m,t,u;byte[]a;int k=2,b,d;E(){a=new byte[5];a[1]=13;}E(E
x){a=Arrays.copyOf(x.a,n+1);k=x.k;d=x.d;b=x.b;}int
g(int x){return(a[x]+1)%3-1;}void s(int x,int y){a[x]=(byte)(a[x]/3*3+(y+3)%3);}void
S(int x,int y){a[x]=(byte)(a[x]%3+(y+3)*3);}E
w(int x){if(g(k)==-x)return null;E e=new E(this);e.s(k,x);e.S(e.k++,x);for(m=0;++m<k;)if(k%m<1){u=e.a[m]/3-3+x;if(u==(k<9?2:4)*x)return
null;e.S(m,u);if(u==3*x){e.b++;if(k+m<=n){if(e.g(k+m)==x)return
null;e.s(k+m,-x);}}}return e;}public int compareTo(E o){m=d-o.d+(b-o.b)/60+(o.k-k)/150;return
m==0?o.k-k:m;}public static void main(String[]a){n=Integer.valueOf(a[0]);Queue<E>q=new PriorityQueue<>();q.add(new
E());for(;;){E x=q.remove(),y;if(x.k>n){for(t=0;++t<x.k;)System.out.print((x.g(t)+1)/2);return;}t=x.g(x.k<9?1:x.k%9==0?x.k/9:x.k%9);y=x.w(t);if(y!=null)q.add(y);y=x.w(-t);if(y!=null){y.d++;q.add(y);}}}}

Ввод принимается в качестве аргумента командной строки.

Это пробует почти все возможности (единственное ограничение - первые 8 шагов должны идти только в пределах -1..1), шаг за шагом, используя магическую эвристику вуду, чтобы выбрать, какой способ попробовать первым.

Он решает 2000 и даже 4000 за 1 секунду на моем (довольно быстром) компьютере. Нужно больше оперативной памяти для больших чисел; самый большой ввод, который я решил в пределах 8 ГБ - 5023, и это заняло около 30 секунд

Десятичное представление решения на 2000 шагов, необходимое для бонуса:

67629177464446960798008264442022667063957880432486338092706841703491740570274032860458934082821213021464065304260003487277917407152662394728833698812373924467640518368465012204980858438160127647802572983143425507448999967241207186701518207195015015739598846687434709056793597015487555707466358473564611432637890414593517116857771284711814076853125419306285869381974622557155019992727242896503018802441210966188045211779436703341152749688824296759097963388158731237092792251164105828728858516951458791084595247591674731645830905744761534078963607725435881491831508342871545788662307953494333833994658998

Добавьте Ybк нему в CJam, чтобы преобразовать обратно в двоичный файл.

Об эвристике: во-первых, есть шаблон, который я использую: каждые 9 шагов пытаются повторить первые 9, за исключением того, что каждый (9 * x) -й шаг пытается повторить x-й шаг. Это основано на решении, которое я скачал и использовал (жестко запрограммировал) в своем ответе на python.

Я отслеживаю количество раз, которое я отклонялся от образца, а также количество раз, когда я достигал "края" (1 шаг от смерти). Эвристическая функция - это, в основном, взвешенная комбинация этих двух чисел и количества предпринятых шагов.

Эвристика может быть дополнительно улучшена для улучшения скорости, и есть несколько способов добавить в нее случайный фактор.
На самом деле, я только что прочитал о мультипликативных функциях в связи с этой проблемой, и похоже, что это может обеспечить значительное улучшение (TODO: реализовать это позже).

Развернулся и прокомментировал:

import java.util.*;

public class Erdos implements Comparable<Erdos> {
    static int n; // input (requested number of steps)
    static int m, t, u; // auxiliary variables

    byte[] a; // keeps each step and sum combined into 1 byte
    int k = 2; // number of steps + 1 (steps are 1-based)
    int edge; // number of times we got to an edge
    int diff; // number of differences from the expected pattern

    // start with one step
    Erdos() {
        a = new byte[5];
        set(1, 1);
        setSum(1, 1);
    }

    // copy constructor
    Erdos(Erdos x) {
        a = Arrays.copyOf(x.a, n + 1);
        k = x.k;
        diff = x.diff;
        edge = x.edge;
    }

    // get the x'th step (can be -1, 0 or 1)
    int get(int x) {
        return (a[x] + 1) % 3 - 1;
    }

    // set the x'th step
    void set(int x, int y) {
        a[x] = (byte) (a[x] / 3 * 3 + (y + 3) % 3);
    }

    // get the sum of every x'th step (should be within -3..3)
    int getSum(int x) {
        return a[x] / 3 - 3;
    }

    // set the sum of every x'th step
    void setSum(int x, int y) {
        a[x] = (byte) (a[x] % 3 + (y + 3) * 3);
    }

    // try to add a step with value x (1 or -1)
    Erdos grow(int x) {
        if (get(k) == -x) // predetermined step doesn't match
            return null;
        Erdos e = new Erdos(this);
        e.set(k, x);
        e.setSum(e.k++, x);
        for (m = 0; ++m < k;)
            if (k % m < 1) { // check all divisors of k
                u = e.getSum(m) + x; // updated sum
                if (u == (k < 9 ? 2 : 4) * x) // use limit 2 for the first 8 steps, 4 for the rest
                    return null; // dead
                e.setSum(m, u);
                if (u == 3 * x) { // we're at an edge
                    e.edge++;
                    if (k + m <= n) { // predetermine future step - should be going back
                        if (e.get(k + m) == x) // conflict
                            return null;
                        e.set(k + m, -x);
                    }
                }
            }
        return e;
    }

    public int compareTo(Erdos o) { // heuristic function
        m = diff - o.diff + (edge - o.edge) / 60 + (o.k - k) / 150;
        return m == 0 ? o.k - k : m;
    }

    public static void main(String[] a) {
        n = Integer.valueOf(a[0]);
        Queue<Erdos> q = new PriorityQueue<>();
        q.add(new Erdos());
        for (;;) {
            Erdos x = q.remove(), y;
            if (x.k > n) { // we made it
                for (t = 0; ++t < x.k;)
                    System.out.print((x.get(t) + 1) / 2);
                return;
            }
            t = x.get(x.k < 9 ? 1 : x.k % 9 == 0 ? x.k / 9 : x.k % 9); // next step based on the pattern
            y = x.grow(t);
            if (y != null)
                q.add(y);
            y = x.grow(-t);
            if (y != null) {
                y.diff++;
                q.add(y);
            }
        }
    }
}
aditsu
источник
«позже» ждет больше года
CalculatorFeline
1

Python 2, 236 байт

n=input();r=len;u=[("",[0]*(n//4))]
while n>r(u[-1][0]):
 y,t=u.pop()
 for c in 0,1:
  s=t[:];u+=(y+"LR"[c],s),
  for i in range(r(s)):
   if-~r(y)//-~i*-~i==-~r(y):s[i]+=2*c-1;
   if abs(s[i])>3:u.pop();break;
print(u[-1][0])

Это довольно быстро, для метода грубой силы, он занимает всего несколько секунд для n = 223, но намного дольше для n> = 224.

Объяснение: Отслеживайте список пар строк-списков (s, u), где список u таков, что u [i] является текущей позицией после выполнения каждого i-го шага в строке. Для каждой строки в списке попробуйте добавить «L» или «R», а затем измените значения в списке, которые пересекаются. (т.е. если полученная строка имеет длину 10, добавьте или вычтите 1 из позиций 1,2,5 и 10, в соответствии с направлениями, которые вы переместили). Если вы превысите 3 или -3, выбросьте новую пару, в противном случае оставьте ее в списке. Самые длинные строки сохраняются в конце. Получив строку длины n, верните ее.

Фрикативная дыня
источник
Почему Python 2/3?
Rɪᴋᴇʀ
Это работает одинаково в любом из них. Должен ли я указать один из них?
Фрикативная дыня
Вероятно, вам следует. Мне было просто интересно, потому что я не знал, что //было доступно в Python 2.
Rɪᴋᴇʀ
-2

Python 2, 729 байт

n=0
for x in"eJytVU2LwyAQPWzTvZjjspcsxFYTBdNuQSEF+///1jp+p5o0hYVSBl9nfOObNz1MlAgqzMcEEwQkDyIkFpDYCW0UnChbyZJiK2sfhDcYmu9hT0GdIPQvLduAmoCvvqEssvq84CVCpLzrNcOOspLhY6/KswB6FmoSxGPBcWts7lsMp/0q83da1hgC6k7GoqBir1ruAFIVvWIdTi++oGIAyZw8mkuG03uDDc+rEsSWTmFBwbLgtTF8hl1e/lpCigR7+pM5V9lIqVJBjStzKNRRQDp6UOrvwga6VFrGcWz6YHwLNYWUYeZfWO/DQTq7i4dAxixeszmtFEw7Cr5v9R3lRVF55TDzY6QRrSfzF9NLE7lAZ+vLnGgYLZ/FlCuoRcOugeFduHTqRWmyh1J91XpIndIbEk8jifL8hs8qQ8vjAVoGqhK5Tm/O5svpXd82QH4Azq05kYnhj93PzLbcTisFzXWfDqIC5zsq3jU7UUhSh1R3L4+i4HCXKlrGyywSBttPr2zpL4gCDPtk2HPN5tgZFomzSDPfGAlASus+e4KlLcjS0vPQ0f5/mR/r1s4PcxsgMLRSMp617AveCuup2OCAPBT6yltWrPO9azsbp6fphR87Lc7VzcbEt5F4Ydg/NzhXTA==".decode("base64").decode("zip"):n=n*64+ord(x)
print bin(n)[2:input()+2]

Я думаю, что это также относится и к бонусу, если идея состоит в том, чтобы «вознаграждать ответы, которые заканчиваются быстро».

Тем не менее, это жестко запрограммированный ответ, который не соответствует духу задачи (хотя он явно не запрещен, когда я его написал).

aditsu
источник
2
Наконец-то ответ! d ;-)
wizzwizz4