Double Cola Challenge, неправильный код JavaScript?

Пятеро друзей в очереди пьют волшебную колу. Когда первый друг выпивает колу, он исчезает и размножается в двух экземплярах! После этого новые копии отправляются в конец очереди, и следующий друг выпивает волшебную колу, повторяя процесс.

Например, представьте, что у нас есть следующие друзья:

[Sheldon, Leonard, Penny, Rajesh, Howard]

После того, как Шелдон выпьет первую колу, строка будет выглядеть так:

[Leonard, Penny, Rajesh, Howard, Sheldon, Sheldon]

После того, как Леонард выпил колу, фраза становится такой:

[Penny, Rajesh, Howard, Sheldon, Sheldon, Leonard, Leonard]

И так далее...

Моя цель — написать функцию на JavaScript, которая, учитывая массив с именами людей в строке и числом N, вернет имя N-го человека, пьющего волшебную колу.

Так, например, выполнение console.log(whoIsNext([Sheldon, Leonard, Penny, Rajesh, Howard], 1)) должно вернуть Sheldon.

Для этого я сделал этот код:

function whoIsNext(names, r){
  var fistInLine;

  if(r <= names.length){
    return names[r-1];
  }else{

    while(r > names.length){
      fistInLine = names.shift();
      names.push(fistInLine, fistInLine);
    }
    return names[r-1];
  }
}

Эта функция хорошо работает для следующего случая:

names = ["Sheldon", "Leonard", "Penny", "Rajesh", "Howard"];
Test.assertEquals(whoIsNext(names, 1), "Sheldon");

Но это не для теста:

names = ["Sheldon", "Leonard", "Penny", "Rajesh", "Howard"];
Test.assertEquals(whoIsNext(names, 52), "Penny");

И если я попробую с действительно большим числом, например:

names = ["Sheldon", "Leonard", "Penny", "Rajesh", "Howard"];
Test.assertEquals(whoIsNext(names, 7230702951), "Leonard");

Он даже не перестает работать (занимает вечность).

Итак, очевидно, что мое решение не только неверно, но и кажется неэффективным. Как я могу это исправить?


person Flame_Phoenix    schedule 05.04.2016    source источник
comment
Почему ты нажимаешь дважды?   -  person epascarello    schedule 05.04.2016
comment
@epascarello Потому что это не простая очередь - при удалении спереди запись дублируется в конце. Вот что делает головоломку сложнее.   -  person James Thorpe    schedule 05.04.2016
comment
Я пропустил это... кофе не подействовал.   -  person epascarello    schedule 05.04.2016
comment
Почему бы просто не использовать математический подход? В конце концов, вы всегда добавляете вдвое больше имен каждый раз, когда вы проходите через всех ... Должен быть четкий шаблон, чтобы его можно было распознать, который, вероятно, можно было бы рассчитать, получив наивысшую комбинацию на степень 2 меньше, чем ваш контрольный номер. Оттуда вычислите обратно, пока не получите число от 0 до 4 и не получите индекс. Не должно быть необходимости вычислять его полностью.   -  person Aides    schedule 05.04.2016
comment
@JamesThorpe Мне не нужно уменьшать r, потому что длина массива продолжает расти. В конце концов, r станет меньше длины массива, и когда это произойдет, я остановлюсь.   -  person Flame_Phoenix    schedule 05.04.2016
comment
@Flame_Phoenix Да, конечно   -  person James Thorpe    schedule 05.04.2016
comment
@Aides Я тоже думал о математическом шаблоне, но мне это совершенно не нравится. Есть ли у вас какие-либо предложения?   -  person Flame_Phoenix    schedule 05.04.2016
comment
Сейчас на работе, посмотрю примерно через... часа 2 или около того.   -  person Aides    schedule 05.04.2016
comment
Как именно вы определили, что номер 52 должен быть Пенни?   -  person Álvaro González    schedule 05.04.2016
comment
@Aides, которые будут достойны похвалы++. На самом деле, я даю вам реп++ за инициативу!   -  person Flame_Phoenix    schedule 05.04.2016
comment
@AlvaroGonzález Человек, придумавший вызов. Так что я предполагаю, что он прав :D   -  person Flame_Phoenix    schedule 05.04.2016
comment
Это онлайн? Можем ли мы получить доступ к тестам или результатам образцов или что-то в этом роде?   -  person Álvaro González    schedule 05.04.2016


Ответы (2)


Рекурсивное предложение с отсчетом от нуля, которое возвращает индекс массива, здесь с длиной base = 5.

                           1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3
number 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
index  0 1 2 3 4 0 0 1 1 2 2 3 3 4 4 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 0 0 0 0 0

Стало видно, что паттерн основан на 5 и увеличивается с каждым раундом на коэффициент 2.

5 -> 10- > 20 -> 40

Пример расчета

                Array after step
                0 1 2 3 4 5 6 7 8 9 
  0: 0 Sheldon  | 
  1: 1 Leonard  | |
  2: 2 Penny    | | |
  3: 3 Rajesh   | | | |
  4: 4 Howard   | | | | |
  5: 0 Sheldon    | | | | |
  6: 0 Sheldon    | | | | | |
  7: 1 Leonard      | | | | | |
  8: 1 Leonard      | | | | | | |
  9: 2 Penny          | | | | | |
 10: 2 Penny          | | | | | |
 11: 3 Rajesh           | | | | |
 12: 3 Rajesh           | | | | |
 13: 4 Howard             | | | |
 14: 4 Howard             | | | |
 15: 0 Sheldon              | | |
 16: 0 Sheldon              | | |
 17: 0 Sheldon                | |
 18: 0 Sheldon                | |
 19: 1 Leonard                  |
 20: 1 Leonard                  |
 21: 1 Leonard
 22: 1 Leonard

var friends = ['Sheldon', 'Leonard', 'Penny', 'Rajesh', 'Howard'],
    base = friends.length;

function getIndex(n, i) {
    i = i || base;
    if (n < i) {
        return Math.floor(n * base / i);
    }
    return getIndex(n - i, 2 * i);
}

var i = 0, index;

document.write(friends[getIndex(1 - 1)] + '<br>');          // "Sheldon"
document.write(friends[getIndex(52 - 1)] + '<br>');         // "Penny"
document.write(friends[getIndex(7230702951 - 1)] + '<hr>'); // "Leonard"

for (i = 0; i < 200; i++) {
    index = getIndex(i);
    document.write(i + ': ' + index + ' ' + friends[index] + '<br>');
}

person Nina Scholz    schedule 05.04.2016
comment
Учитывает ли ваша функция тот факт, что первое имя дублируется, а остальные нет? - person Flame_Phoenix; 05.04.2016
comment
Кроме того, размер массива не удваивается каждый раунд. Я не понимаю, как это делается. Он просто увеличивается на 1 значение. Подумайте об этом, вы удаляете одно значение из очереди и добавляете 2. Общее смещение равно +1. Остальное остается прежним. Так что если в массиве 5 имен, то после первого запуска их будет 6, а не 10! - person Flame_Phoenix; 05.04.2016
comment
@Flame_Phoenix, где ты видишь 10? - person Nina Scholz; 05.04.2016
comment
Аааа, понял! Это оно! - person Flame_Phoenix; 05.04.2016

Хорошо, поехали, это действительно упрощенный подход, и я придумаю вариант получше (это на полпути в моей голове, мне просто нужно собрать его воедино)

function whoIsNext(names, index, multiplyFactor)
{
    var count = names.length;

    var fullLoops = 0;
    var currIndex = 0;

    while(currIndex <= index)
    {
        for(var i = 0; i < count; i++)
    {
            currIndex += Math.pow(multiplyFactor, fullLoops);
            if(currIndex > index)
            {
            return names[i];
            }
        }
        fullLoops++;
    }
}

Идея состоит в том, что сумма, которую приходит один и тот же человек, удваивается каждый раз, когда люди делают полный цикл (countPerson = Math.pow(2, countFullLoops)). Если вы затем накапливаете количество людей до установленного индекса, пока не достигнете индекса, вы получите нужного человека (мне кажется, что я только что очень сложно объяснил очень простую вещь).

Также вы можете подставить любые входные данные (изменить количество людей на старте, изменить коэффициент умножения (кто-то сказал четверной кокаин?)) как хотите.

person Aides    schedule 05.04.2016