Школьная загадка про сейф, которая ставит в тупик большинство взрослых

На собе­се­до­ва­ни­ях в круп­ные ком­па­нии любят зада­вать мате­ма­ти­че­ские задач­ки. Обыч­ные люди пыта­ют­ся решить их на листах бума­ги, но, если вла­деть про­сты­ми тех­ни­че­ски­ми навы­ка­ми, вы смо­же­те най­ти ответ за секун­ду. Для нача­ла — задача:

Отец умер, и всё его иму­ще­ство пере­шло к жене и детям. Сре­ди иму­ще­ства был сейф, где он дер­жал свои самые важ­ные бума­ги и дра­го­цен­но­сти. Детям страш­но хоте­лось попасть в этот сейф. У сей­фа был сек­рет: если непра­виль­но вве­сти шифр, всё содер­жи­мое сра­зу сжи­га­ет­ся. Сам кодо­вый замок выгля­дел как квад­рат из 16 яче­ек, в кото­рые нуж­но вве­сти числа. Дети назва­ли несколь­ко чисел, кото­рые они запом­ни­ли, под­смат­ри­вая за отцом, но осталь­ных они не зна­ли. Мать вспом­ни­ла стран­ность отцов­ско­го шиф­ра: сум­ма всех чисел по каж­дой гори­зон­та­ли, вер­ти­ка­ли и двум глав­ным диа­го­на­лям рав­ня­лась 50. Всё услож­ня­лось тем, что мож­но было вво­дить толь­ко чис­ла от 5 до 20 без повто­ре­ний — осо­бен­ность семей­но­го сейфа.
Помо­ги­те род­ствен­ни­кам вве­сти недо­ста­ю­щие чис­ла с пер­во­го раза, ина­че всё наслед­ство будет уни­что­же­но систе­мой без­опас­но­сти сейфа.

Решение

Обыч­но люди реша­ют эту зада­чу про­стым пере­бо­ром: под­став­ля­ют циф­ры в раз­ные ячей­ки и ищут то их соче­та­ние, кото­рое будет соот­вет­ство­вать всем условиям.

Но пере­бор — это иде­аль­ная зада­ча для ком­пью­те­ра. Напи­шем про­стой алго­ритм, кото­рый пере­бе­рёт все вари­ан­ты кодов и най­дёт нуж­ное сочетание.

Решим эту зада­чу мето­дом пол­но­го пере­бо­ра. Это зна­чит, что мы по оче­ре­ди будем под­став­лять вме­сто иксов на кар­тин­ке все чис­ла в диа­па­зоне от 5 до 20 в каж­дую ячей­ку, а потом про­ве­рять, выпол­ня­ют­ся усло­вия зада­чи или нет.

Матчасть про массивы

Для реше­ния нам пона­до­бит­ся новый тип пере­мен­ной — массив.

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

Cтел­ла­жи — при­мер одно­мер­но­го мас­си­ва. Одно­мер­но­го — зна­чит, что вам неваж­но, на какой кон­крет­ной пол­ке лежит чес­нок, доста­точ­но знать, что он на стел­ла­же номер 0. На стел­ла­же номер 1 — кон­фе­ты, а под номе­ром 2 — стел­лаж с чаем. Это­го доста­точ­но, что­бы послать груз­чи­ка за чаем, если вам неваж­но, какой чай он при­не­сёт. Одно­мер­ность озна­ча­ет, что нам нуж­но знать один толь­ко номер стел­ла­жа, без номе­ров полок.

Дву­мер­ный мас­сив — когда важ­но назвать груз­чи­ку не толь­ко номер стел­ла­жа, но и номер пол­ки, с кото­ро­го взять чай. Трёх­мер­ный — когда нужен ещё и номер нуж­ной под­соб­ки и так далее. Чем слож­нее систе­ма раз­ме­ще­ния про­дук­тов на скла­де, тем мно­го­мер­нее мас­сив. Если нуж­ный нам чай хра­нит­ся в самой пер­вой под­соб­ке на вто­ром стел­ла­же, а в нём — на чет­вёр­той пол­ке, то на язы­ке JavaScript это бы выгля­де­ло так:

Склад[ подсобка_0 ][ стеллаж_1 ][ полка_3 ] = 'чай байховый';

Не забы­вай­те, что всё счи­та­ет­ся начи­ная с нуля, поэто­му пер­вая под­соб­ка пре­вра­ти­лась в подсобку_0, а чет­вёр­тая пол­ка — в полку_3.

Практика с массивами

Давай­те посмот­рим на при­ме­ры в JavaScript. Нач­нём с одно­мер­но­го мас­си­ва из шести целых чисел:

var massiv = [4,6,1,3,5,2];

Что­бы выве­сти такой мас­сив на экран, исполь­зу­ем цикл от 0 до 5 — все­го 6 шагов. Помни­те, что нуме­ра­ция в мас­си­вах начи­на­ет­ся с нуля:

for (var i = 0; i < 6; i++) {

 

// каждый элемент массива — на новой строке

console.log(massiv[i]);

}

Теперь давай­те попро­бу­ем сде­лать дву­мер­ный мас­сив. В нём пер­вым будет идти чис­ло, вто­рым — назва­ние это­го чис­ла по-русски:

var massiv_2 = [

[1,'Один'],

[2,'Два'],

[3,'Три'],

[4,'Четыре'],

[5,'Пять'],

[6,'Шесть'],

]

Что­бы выве­сти, напри­мер, сло­во «Один», нам нужен эле­мент под номе­ром 0 (нуме­ра­ция — с нуля, а не с еди­ни­цы), а в нём — ячей­ка под номе­ром 1:

console.log(massiv_2[0][1]);

А что­бы выве­сти чис­ло 5 — чет­вёр­тый эле­мент, нуле­вая ячейка:

console.log(massiv_2[4][0]);

Давай­те выве­дем на экран все зна­че­ния всех яче­ек наше­го дву­мер­но­го мас­си­ва. Для это­го возь­мём вло­жен­ный цикл: пер­вый будет отве­чать за номе­ра эле­мен­тов, вто­рой — за номе­ра ячеек:

//i — номер элемента, от 0 до 5
for (var i = 0; i < 6; i++) {
  //j — номер ячейки, от 0 до 1
  for (var j = 0; j < 2; j++) {
    //на каждом шаге цикла значения i и j меняются
    console.log(massiv_2[i][j]);
  }
}

Вот так про­сто рабо­тать с мас­си­ва­ми. Теперь мы можем исполь­зо­вать их для реше­ния нашей задачи.

Решение: объявляем переменные

Пер­вое, что нам пона­до­бит­ся, — мас­сив, где будет хра­нить­ся ком­би­на­ция шиф­ра. Так как мы уже зна­ем неко­то­рые чис­ла, то сра­зу под­ста­вим их в нуж­ное место. Осталь­ные сде­ла­ем нуле­вы­ми, а в про­цес­се реше­ния будем под­став­лять на их место новые значения:

var code=[ [0,15,0,5], [17,0,11,0], [0,0,0,0], [14,9,0,0], ];

Смот­ри­те, наш мас­сив — дву­мер­ный и состо­ит как бы из строк и столб­цов. Зная это, мы можем рабо­тать с каж­дым эле­мен­том мас­си­ва как с ячей­кой таб­ли­цы. Заве­дём отдель­ные пере­мен­ные для циклов:

var i1,i2,i3,i4,i5,i6,i7,i8,i9,i10;

И сде­ла­ем отдель­ную пере­мен­ную, кото­рая будет сле­дить, нашли мы реше­ние или нет. Сде­ла­ем её сна­ча­ла нуле­вой, а как толь­ко най­дём ком­би­на­цию — запи­шем в неё еди­ни­цу. Если к кон­цу рабо­ты про­грам­мы в пере­мен­ной так и оста­нет­ся ноль, зна­чит, реше­ния мы не нашли и сейф нам не открыть.

var code_exist=0;

Логика работы и цикл

Реше­ние будет таким:

  1. Так как мы не зна­ем 10 чисел в шиф­ре, сде­ла­ем 10 вло­жен­ных цик­лов и будем из них брать зна­че­ния для подстановки.
  2. Под­став­ля­ем эти зна­че­ния в наш шифр.
  3. Про­ве­ря­ем все усло­вия, кото­рые есть в задаче.
  4. Если все усло­вия выпол­ни­лись — выво­дим резуль­тат и поме­ча­ем в пере­мен­ной, что реше­ние найдено.

Так как у нас 10 неиз­вест­ных чисел, нам пона­до­бит­ся 10 цик­лов, вло­жен­ных друг в дру­га. Это нуж­но для того, что­бы пере­брать все воз­мож­ные ком­би­на­ции. По усло­вию, чис­ла лежат в диа­па­зоне от 5 до 20 вклю­чи­тель­но, но в таб­ли­це уже есть чис­ло 5 и повто­рять­ся оно не может. Зна­чит, нам нужен диа­па­зон помень­ше — от 6 до 20.

Сде­ла­ем 10 вло­жен­ных цик­лов, исполь­зуя отдель­ную пере­мен­ную для каж­до­го цик­ла. Не забы­вай­те, нам нуж­ны цик­лы с чис­лом 20 вклю­чи­тель­но, поэто­му усло­вие цик­ла — пере­мен­ная долж­на быть мень­ше 21.

for (i1 = 6; i1 < 21; i1++) {
  for(i2 =6; i2 <21; i2++) {
    for(i3 =6; i3 <21; i3++) {
      for(i4 =6; i4 <21; i4++) {
        for(i5 =6; i5 <21; i5++) {
          for(i6 =6; i6 <21; i6++) {
            for(i7 =6; i7 <21; i7++) {
              for(i8 =6; i8 <21; i8++) {
                for(i9 =6; i9 <21; i9++) {
                  for(i10 =6; i10 <21; i10++) {
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
console.log('i1= ' + i1);

В пред­по­след­нем цик­ле мы доба­ви­ли вывод теку­ще­го зна­че­ния пере­мен­ной i1. Это свя­за­но с тем, что пол­ный пере­бор — ресур­со­ём­кая зада­ча, кото­рая может зани­мать десят­ки минут. Что­бы не было ощу­ще­ния, что ком­пью­тер завис, будем по оче­ре­ди выво­дить зна­че­ние пер­вой пере­мен­ной — её изме­не­ние пока­жет, что зада­ча всё ещё решается.

Все осталь­ные коман­ды будем поме­щать внутрь само­го послед­не­го, деся­то­го цикла.

Подстановка значений в шифр

После каж­до­го шага берём зна­че­ния всех пере­мен­ных и под­став­ля­ем их на нуж­ные места вме­сто нулей в наш шифр. Помни­те, что нуме­ра­ция строк и столб­цов начи­на­ет­ся с нуля, а не с единицы:

code[0][0]=i1; code[0][2]=i2; code[1][1]=i3; code[1][3]=i4; code[2][0]=i5; code[2][1]=i6; code[2][2]=i7; code[2][3]=i8; code[3][2]=i9; code[3][3]=i0;

Проверяем полученное решение

На каж­дом шаге цик­ла нуж­но про­ве­рять, соот­вет­ству­ет ли наше реше­ние началь­ным усло­ви­ям. Напом­ним условия:

Сде­ла­ем все эти про­вер­ки внут­ри одно­го усло­вия. Нач­нём с про­вер­ки всех горизонталей:

(code[0][0]+code[0][1]+code[0][2]+code[0][3] == 50) &&  (code[1][0]+code[1][1]+code[1][2]+code[1][3] == 50) &&  (code[2][0]+code[2][1]+code[2][2]+code[2][3] == 50) &&  (code[3][0]+code[3][1]+code[3][2]+code[3][3] == 50)

Затем вер­ти­ка­лей:

(code[0][0]+code[1][0]+code[2][0]+code[3][0] == 50) &&  (code[0][1]+code[1][1]+code[2][1]+code[3][1] == 50) &&  (code[0][2]+code[1][2]+code[2][2]+code[3][2] == 50) &&  (code[0][3]+code[1][3]+code[2][3]+code[3][3] == 50)

Про­ве­рим диагонали:

(code[0][0]+code[1][1]+code[2][2]+code[3][3] == 50) && (code[0][3]+code[1][2]+code[2][1]+code[3][0] == 50)

И про­ве­рим, что­бы не было двух оди­на­ко­вых чисел. Для это­го по оче­ре­ди срав­ним каж­дое зна­че­ние эле­мен­та шиф­ра со все­ми осталь­ны­ми по оче­ре­ди. Этот код не очень эле­гант­ный, но делать эле­гант­но сей­час не хочет­ся, ведь мы хотим заста­вить рабо­тать маши­ну, а не себя.

Неэлегантная проверка уникальности

(code[0][0] != code[0][1]) && (code[0][0] != code[0][2]) && (code[0][0] != code[0][3]) && (code[0][0] != code[1][0]) && (code[0][0] != code[1][1]) && (code[0][0] != code[1][2]) && (code[0][0] != code[1][3]) && (code[0][0] != code[2][0]) && (code[0][0] != code[2][1]) && (code[0][0] != code[2][2]) && (code[0][0] != code[2][3]) && (code[0][0] != code[3][0]) && (code[0][0] != code[3][1]) && (code[0][0] != code[3][2]) && (code[0][0] != code[3][3]) &&
  (code[0][1] != code[0][2]) && (code[0][1] != code[0][3]) && (code[0][1] != code[1][0]) && (code[0][1] != code[1][1]) && (code[0][1] != code[1][2]) && (code[0][1] != code[1][3]) && (code[0][1] != code[2][0]) && (code[0][1] != code[2][1]) && (code[0][1] != code[2][2]) && (code[0][1] != code[2][3]) && (code[0][1] != code[3][0]) && (code[0][1] != code[3][1]) && (code[0][1] != code[3][2]) && (code[0][1] != code[3][3]) &&

  (code[0][2] != code[0][3]) && (code[0][2] != code[1][0]) && (code[0][2] != code[1][1]) && (code[0][2] != code[1][2]) && (code[0][2] != code[1][3]) && (code[0][2] != code[2][0]) && (code[0][2] != code[2][1]) && (code[0][2] != code[2][2]) && (code[0][2] != code[2][3]) && (code[0][2] != code[3][0]) && (code[0][2] != code[3][1]) && (code[0][2] != code[3][2]) && (code[0][2] != code[3][3]) &&

  (code[0][3] != code[1][0]) && (code[0][3] != code[1][1]) && (code[0][3] != code[1][2]) && (code[0][3] != code[1][3]) && (code[0][3] != code[2][0]) && (code[0][3] != code[2][1]) && (code[0][3] != code[2][2]) && (code[0][3] != code[2][3]) && (code[0][3] != code[3][0]) && (code[0][3] != code[3][1]) && (code[0][3] != code[3][2]) && (code[0][3] != code[3][3]) &&

  (code[1][0] != code[1][1]) && (code[1][0] != code[1][2]) && (code[1][0] != code[1][3]) && (code[1][0] != code[2][0]) && (code[1][0] != code[2][1]) && (code[1][0] != code[2][2]) && (code[1][0] != code[2][3]) && (code[1][0] != code[3][0]) && (code[1][0] != code[3][1]) && (code[1][0] != code[3][2]) && (code[1][0] != code[3][3]) &&

  (code[1][1] != code[1][2]) && (code[1][1] != code[1][3]) && (code[1][1] != code[2][0]) && (code[1][1] != code[2][1]) && (code[1][1] != code[2][2]) && (code[1][1] != code[2][3]) && (code[1][1] != code[3][0]) && (code[1][1] != code[3][1]) && (code[1][1] != code[3][2]) && (code[1][1] != code[3][3]) &&

  (code[1][2] != code[1][3]) && (code[1][2] != code[2][0]) && (code[1][2] != code[2][1]) && (code[1][2] != code[2][2]) && (code[1][2] != code[2][3]) && (code[1][2] != code[3][0]) && (code[1][2] != code[3][1]) && (code[1][2] != code[3][2]) && (code[1][2] != code[3][3]) &&

  (code[1][3] != code[2][0]) && (code[1][3] != code[2][1]) && (code[1][3] != code[2][2]) && (code[1][3] != code[2][3]) && (code[1][3] != code[3][0]) && (code[1][3] != code[3][1]) && (code[1][3] != code[3][2]) && (code[1][3] != code[3][3]) &&

  (code[2][0] != code[2][1]) && (code[2][0] != code[2][2]) && (code[2][0] != code[2][3]) && (code[2][0] != code[3][0]) && (code[2][0] != code[3][1]) && (code[2][0] != code[3][2]) && (code[2][0] != code[3][3]) &&

  (code[2][1] != code[2][2]) && (code[2][1] != code[2][3]) && (code[2][1] != code[3][0]) && (code[2][1] != code[3][1]) && (code[2][1] != code[3][2]) && (code[2][1] != code[3][3]) &&

  (code[2][2] != code[2][3]) && (code[2][2] != code[3][0]) && (code[2][2] != code[3][1]) && (code[2][2] != code[3][2]) && (code[2][2] != code[3][3]) &&

  (code[2][3] != code[3][0]) && (code[2][3] != code[3][1]) && (code[2][3] != code[3][2]) && (code[2][3] != code[3][3]) &&

  (code[3][0] != code[3][1]) && (code[3][0] != code[3][2]) && (code[3][0] != code[3][3]) &&

  (code[3][1] != code[3][2]) && (code[3][1] != code[3][3]) &&

  (code[3][2] != code[3][3])

Выводим правильную комбинацию

Сей­час наш услов­ный опе­ра­тор пустой: если все усло­вия выпол­нят­ся, то всё рав­но ниче­го не про­изой­дёт. Давай­те это испра­вим и доба­вим вывод реше­ния на экран:

console.log('Unlocked!'); console.log(code[0][0]+' '+code[0][1]+' '+code[0][2]+' '+code[0][3]); console.log(code[1][0]+' '+code[1][1]+' '+code[1][2]+' '+code[1][3]); console.log(code[2][0]+' '+code[2][1]+' '+code[2][2]+' '+code[2][3]); console.log(code[3][0]+' '+code[3][1]+' '+code[3][2]+' '+code[3][3]);

Собираем код в одно целое

Что­бы про­грам­ма рабо­та­ла пра­виль­но, надо все коман­ды запи­сать в нуж­ной последовательности:

Итоговый код

var code = [
  [0, 15, 0, 5],
  [17, 0, 11, 0],
  [0, 0, 0, 0],
  [14, 9, 0, 0],
];
var i1, i2, i3, i4, i5, i6, i7, i8, i9, i10;
var code_exist = 0;
for (i1 = 6; i1 < 21; i1++) {
  for (i2 = 6; i2 < 21; i2++) {
    for (i3 = 6; i3 < 21; i3++) {
      for (i4 = 6; i4 < 21; i4++) {
        for (i5 = 6; i5 < 21; i5++) {
          for (i6 = 6; i6 < 21; i6++) {
            for (i7 = 6; i7 < 21; i7++) {
              for (i8 = 6; i8 < 21; i8++) {
                for (i9 = 6; i9 < 21; i9++) {
                  for (i10 = 6; i10 < 21; i10++) {
                    code[0][0] = i1;
                    code[0][2] = i2;
                    code[1][1] = i3;
                    code[1][3] = i4;
                    code[2][0] = i5;
                    code[2][1] = i6;
                    code[2][2] = i7;
                    code[2][3] = i8;
                    code[3][2] = i9;
                    code[3][3] = i10;
                    if (
                      (code[0][0] + code[0][1] + code[0][2] + code[0][3] == 50) &&
                      (code[1][0] + code[1][1] + code[1][2] + code[1][3] == 50) &&
                      (code[2][0] + code[2][1] + code[2][2] + code[2][3] == 50) &&
                      (code[3][0] + code[3][1] + code[3][2] + code[3][3] == 50) &&
                      (code[0][0] + code[1][0] + code[2][0] + code[3][0] == 50) &&
                      (code[0][1] + code[1][1] + code[2][1] + code[3][1] == 50) &&
                      (code[0][2] + code[1][2] + code[2][2] + code[3][2] == 50) &&
                      (code[0][3] + code[1][3] + code[2][3] + code[3][3] == 50) &&
                      (code[0][0] + code[1][1] + code[2][2] + code[3][3] == 50) &&
                      (code[0][3] + code[1][2] + code[2][1] + code[3][0] == 50) &&
                      (code[0][0] != code[0][1]) && (code[0][0] != code[0][2]) && (code[0][0] != code[0][3]) && (code[0][0] != code[1][0]) && (code[0][0] != code[1][1]) && (code[0][0] != code[1][2]) && (code[0][0] != code[1][3]) && (code[0][0] != code[2][0]) && (code[0][0] != code[2][1]) && (code[0][0] != code[2][2]) && (code[0][0] != code[2][3]) && (code[0][0] != code[3][0]) && (code[0][0] != code[3][1]) && (code[0][0] != code[3][2]) && (code[0][0] != code[3][3]) &&
                      (code[0][1] != code[0][2]) && (code[0][1] != code[0][3]) && (code[0][1] != code[1][0]) && (code[0][1] != code[1][1]) && (code[0][1] != code[1][2]) && (code[0][1] != code[1][3]) && (code[0][1] != code[2][0]) && (code[0][1] != code[2][1]) && (code[0][1] != code[2][2]) && (code[0][1] != code[2][3]) && (code[0][1] != code[3][0]) && (code[0][1] != code[3][1]) && (code[0][1] != code[3][2]) && (code[0][1] != code[3][3]) &&
                      (code[0][2] != code[0][3]) && (code[0][2] != code[1][0]) && (code[0][2] != code[1][1]) && (code[0][2] != code[1][2]) && (code[0][2] != code[1][3]) && (code[0][2] != code[2][0]) && (code[0][2] != code[2][1]) && (code[0][2] != code[2][2]) && (code[0][2] != code[2][3]) && (code[0][2] != code[3][0]) && (code[0][2] != code[3][1]) && (code[0][2] != code[3][2]) && (code[0][2] != code[3][3]) &&
                      (code[0][3] != code[1][0]) && (code[0][3] != code[1][1]) && (code[0][3] != code[1][2]) && (code[0][3] != code[1][3]) && (code[0][3] != code[2][0]) && (code[0][3] != code[2][1]) && (code[0][3] != code[2][2]) && (code[0][3] != code[2][3]) && (code[0][3] != code[3][0]) && (code[0][3] != code[3][1]) && (code[0][3] != code[3][2]) && (code[0][3] != code[3][3]) &&
                      (code[1][0] != code[1][1]) && (code[1][0] != code[1][2]) && (code[1][0] != code[1][3]) && (code[1][0] != code[2][0]) && (code[1][0] != code[2][1]) && (code[1][0] != code[2][2]) && (code[1][0] != code[2][3]) && (code[1][0] != code[3][0]) && (code[1][0] != code[3][1]) && (code[1][0] != code[3][2]) && (code[1][0] != code[3][3]) &&
                      (code[1][1] != code[1][2]) && (code[1][1] != code[1][3]) && (code[1][1] != code[2][0]) && (code[1][1] != code[2][1]) && (code[1][1] != code[2][2]) && (code[1][1] != code[2][3]) && (code[1][1] != code[3][0]) && (code[1][1] != code[3][1]) && (code[1][1] != code[3][2]) && (code[1][1] != code[3][3]) &&
                      (code[1][2] != code[1][3]) && (code[1][2] != code[2][0]) && (code[1][2] != code[2][1]) && (code[1][2] != code[2][2]) && (code[1][2] != code[2][3]) && (code[1][2] != code[3][0]) && (code[1][2] != code[3][1]) && (code[1][2] != code[3][2]) && (code[1][2] != code[3][3]) &&
                      (code[1][3] != code[2][0]) && (code[1][3] != code[2][1]) && (code[1][3] != code[2][2]) && (code[1][3] != code[2][3]) && (code[1][3] != code[3][0]) && (code[1][3] != code[3][1]) && (code[1][3] != code[3][2]) && (code[1][3] != code[3][3]) &&
                      (code[2][0] != code[2][1]) && (code[2][0] != code[2][2]) && (code[2][0] != code[2][3]) && (code[2][0] != code[3][0]) && (code[2][0] != code[3][1]) && (code[2][0] != code[3][2]) && (code[2][0] != code[3][3]) &&
                      (code[2][1] != code[2][2]) && (code[2][1] != code[2][3]) && (code[2][1] != code[3][0]) && (code[2][1] != code[3][1]) && (code[2][1] != code[3][2]) && (code[2][1] != code[3][3]) &&
                      (code[2][2] != code[2][3]) && (code[2][2] != code[3][0]) && (code[2][2] != code[3][1]) && (code[2][2] != code[3][2]) && (code[2][2] != code[3][3]) &&
                      (code[2][3] != code[3][0]) && (code[2][3] != code[3][1]) && (code[2][3] != code[3][2]) && (code[2][3] != code[3][3]) &&
                      (code[3][0] != code[3][1]) && (code[3][0] != code[3][2]) && (code[3][0] != code[3][3]) &&
                      (code[3][1] != code[3][2]) && (code[3][1] != code[3][3]) &&
                      (code[3][2] != code[3][3])
                    ) {
                      console.log('Unlocked!');
                      console.log(code[0][0] + ' ' + code[0][1] + ' ' + code[0][2] + ' ' + code[0][3]);
                      console.log(code[1][0] + ' ' + code[1][1] + ' ' + code[1][2] + ' ' + code[1][3]);
                      console.log(code[2][0] + ' ' + code[2][1] + ' ' + code[2][2] + ' ' + code[2][3]);
                      console.log(code[3][0] + ' ' + code[3][1] + ' ' + code[3][2] + ' ' + code[3][3]);
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  console.log('i1= ' + i1);
}

Запускаем код в браузере

Запус­ка­ем бра­у­зер Chrome и вызы­ва­ем кон­соль ком­би­на­ци­ей кла­виш Shift+Ctrl+I. Встав­ля­ем наш код в кон­соль, нажи­ма­ем Enter. Через несколь­ко минут вы уви­ди­те нача­ло рабо­ты про­грам­мы — нач­нёт­ся вывод зна­че­ний пере­мен­ной i1. Как толь­ко ком­пью­тер под­бе­рёт реше­ние — тут же выве­дет его на экран и про­дол­жит рабо­ту. Если реше­ние не поме­сти­лось на экране — про­сто про­кру­ти­те вывод резуль­та­тов наверх.

И ТУТ ПРИБЕГАЮТ НАСТОЯЩИЕ ПРОГРАММИСТЫ и кричат «ПЛОХОЙ КОД!»

Тот код, кото­рый у нас полу­чил­ся — при­ми­тив­ный, пло­хо выгля­дит и вызы­ва­ет силь­ное жела­ние его опти­ми­зи­ро­вать. Давай­те пере­пи­шем кусок кода с про­вер­ка­ми, где мы в лоб срав­ни­ва­ем эле­мен­ты мас­си­ва меж­ду собой и про­ве­ря­ем таким же обра­зом сум­му эле­мен­тов в строках.

Оптимизированный для чтения код

// сигнальные переменные, которые следят за тем, все ли условия выполняются. По умолчанию они говорят, что всё верно, но внутри кода, как только будет ошибка при проверке, они превратятся в ложные
var diag = true;
var lines = true;
var uniqie = true;

// здесь мы будем хранить результат вычислений сумм, равны они 50 или нет
var result1, result2, result3, result4 = 0;

// проверяем обе диагонали
for (var j = 0; j < 4; j++) {
  result1 += code[j][j];
  result2 += code[j][3 - j];
}

// если хоть одно из них не 50 — помечаем, что условие не выполнилось
if ((result1 != 50) && (result2 != 50)) { diag = false };

// проверяем сумму в строках и столбцах
for (var j = 0; j < 4; j++) {
  for (var k = 0; k < 4; k++) {

    // суммируем строку и столбец
    result3 += code[j][k];
    result4 += code[k][j];
  }

  // если сумма не та, что нужно — помечаем, что условие в линиях не выполнилось
  if (result3 != 50) { lines = false };
  if (result4 != 50) { lines = false };

  // здесь же — сравниваем каждый элемент массива со всеми остальными
  for (j1 = j; j1 < 4; j1++) {
    for (k1 = k; k1 < 4; k1++) {

      // если нашли равные элементы — помечаем, что эта проверка провалилась
      if (code[j][k] == code[j1][k1]) { uniqie = false };
    }
  }
}

// если все проверки прошли успешно — выводим результат
if (diag && lines && uniqie) {

  console.log('Unlocked!');
  console.log(code[0][0] + ' ' + code[0][1] + ' ' + code[0][2] + ' ' + code[0][3]);
  console.log(code[1][0] + ' ' + code[1][1] + ' ' + code[1][2] + ' ' + code[1][3]);
  console.log(code[2][0] + ' ' + code[2][1] + ' ' + code[2][2] + ' ' + code[2][3]);
  console.log(code[3][0] + ' ' + code[3][1] + ' ' + code[3][2] + ' ' + code[3][3]);

}

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

Поэто­му, не все­гда нуж­но опти­ми­зи­ро­вать код, кото­рый внешне выгля­дит некра­си­во. Если на пер­вом месте — быст­ро­дей­ствие, то ино­гда гру­бый код рабо­та­ет быст­рее, чем кра­си­вый. Такие дела.

А где ответ на задачу?

А отве­та не будет 🙂 Что­бы его узнать, запу­сти­те код само­сто­я­тель­но. Напри­мер, через кон­соль бра­у­зе­ра. И дожди­тесь, когда код выдаст пра­виль­ный ответ.

Что дальше

Код, кото­рый у нас полу­чил­ся, — не самый кра­си­вый и быст­рый, его мож­но опти­ми­зи­ро­вать несколь­ки­ми спо­со­ба­ми. Напри­мер, отка­зать­ся от про­ме­жу­точ­но­го запол­не­ния пере­мен­ной code и срав­ни­вать зна­че­ния пере­мен­ных напря­мую. Есть и дру­гие, более изящ­ные спо­со­бы орга­ни­за­ции пол­но­го перебора.