Wykonujemy proste, acz ciekawe ćwiczenie, które ma nam wypisać ileś wartości cyfr w systemie dwójkowym. Wiadomo, że pierwsza to 1, druga to 2, potem 4,8,16…

Ok, podejście pierwsze:

function valuesInBinary(num){

    let values = [1];

    while(num > 1){
        let currVal = values[0] << 1;
        values = [currVal, ...values];
        num--;
    }

    return values;
}

console.log(valuesInBinary(8));
//[ 128, 64, 32, 16, 8, 4, 2, 1 ]

Jeżeli rozumiemy dlaczego daje wynik jaki daje to już jest bardzo dobrze. Ale programista musi rozumieć dużo więcej z tego, co się dzieje. Nie chodzi o optymalizację, ale o zrozumienie materii.

Zatem pytam:

  • Co w tym algorytmie jest szybkiego?
  • Co w tym algorytmie jest wolnego?

Cóż, szybki jest operator bitowy. Wolne? Przede wszystkim przesuwanie tablicy w prawo. Możemy to sobie rozrysować, ale dodanie elementu na końcu to zawsze O(1). Dodanie elementu na początku to zawsze przeindeksowanie wszystkich pozostałych elementów +1.

Ok, pomyślmy nad czymś innym. Niech używa, a bo ja wiem, Math.pow(2, n). Spróbujmy:

console.log(Array.from({length: 4}, (_, i) => {
    return 1 * Math.pow(2, i);
}))

//[ 1, 2, 4, 8 ]


console.log(Array.from({length: 8}, (_, i) => {
    return 1 * Math.pow(2, i);
}))

//[ 1, 2, 4, 8, 16, 32, 64, 128 ]

W złej kolejności to rośnie, chcemy pokazywać tak jak się pokazuje, czyli 1 najbardziej na prawo. Z drugiej strony chcemy dodawać te liczby w kolejności a nie przesuwać w prawo non-stop tablicę.

Jakiś pomysł? Polecam zawsze metodą prób i błędów. Zobaczmy na te nasze 1…128. Najmniejsza to 1 * 2^0 (1), największa to 1 * 2^7 (128).

Możemy pomyśleć albo potestować na czuja, koniec końców programiście to nie robi różnicy czy zrozumie swój program przed napisaniem czy po napisaniu, ważne żeby wszystko działało i było dogłębnie przetestowane:

console.log(Array.from({length: 8}, (_, i) => {
    return 1 * Math.pow(2, 7-i);
}))
//[ 128, 64, 32, 16, 8, 4, 2, 1 ]

Teraz możemy napisać naszą funkcję:

function valuesInBinary_v2(num){
    return Array.from({length: num}, (_, i) => {
        return 1 * Math.pow(2, num-1-i);
    });
}

console.log(valuesInBinary_v2(8));
//[ 128, 64, 32, 16, 8, 4, 2, 1 ]

No i mamy. Teraz można pod czymś takim podopisywać jedynki i zera i mamy ściągę do łatwiejszego bawienia się z binarnymi, zanim się z nimi oswoimy.