2
\$\begingroup\$

I wrote this code below to get all possible words for an array containing \$n\$ letters.

The logic of the code is that the letters of the array are mixed up in random positions and checks if this combination already exists, if so it continues to look for combinations that are new. The code automatically stops if \$n!\$ words are found.

Are there also other ways to get the same results, for example without random functions?

function getRandom(min, max) {
    const minCeiled = Math.ceil(min);
    const maxFloored = Math.floor(max);
    return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
}

function factorial(m) {
    let num = 1;
    for (let i = 1; i <= m; i++) {
        num = num * i;
    }
    return num;
}



function check(A, B) {
    for (let i = 0; i < A.length; i++) {
        let a = 0;
        for (let j = 0; j < A[i].length; j++) {

            if (A[i][j] == B[j]) {
                a++;
            }
            if (a == B.length) {
                return false
            }

        }

    }
    return true;
}


function getCombinations(A) {
    let x = A.length;
    let B = [];


    while (B.length < factorial(x)) {
        let Y = [];
        for (let i = 0; i < x; i++) {
            let a = getRandom(0, (x));
            if (Y.includes(A[a]) == false) {
                Y.push(A[a]);
            }
            else {
                i = i - 1;
            }
        }


        if (check(B, Y)) {
            B.push(Y);
        }


    }



    console.log(B);


}
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Can you provide an example of what this does, rather than just describing it? Are you actually trying to find words or just unique combinations of letters of a certain length? \$\endgroup\$ Commented Sep 19 at 18:09
  • \$\begingroup\$ @Chris care to guess where John is expecting "n! words"? \$\endgroup\$ Commented Sep 21 at 20:18
  • \$\begingroup\$ Is the array specified to contain (n) distinct characters? Can it contain a charagter more than once? ('aarry') Does every "word" need to be length n? \$\endgroup\$ Commented Sep 21 at 20:23

1 Answer 1

3
\$\begingroup\$

I will only review your algorithm. You could have get rid of randomness and rely on an exact procedure. Below, is my take on your problem:

<!DOCTYPE html>
<html>
<head>
    <title>Letter permutations</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <script>
        const str = "ABCDE";
        
        function swap(arr, i, j) {
            const temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }

        function loadPermutation(letters, indices) {
            const permutation = [];

            for (let i = 0; i < indices.length; ++i) {
                permutation.push(letters[indices[i]]);
            }

            return permutation;
        }

        function generateNextPemutation(letters, indices) {
            let i = indices.length - 2;

            while (i >= 0 && indices[i] > indices[i + 1]) {
                --i;
            }

            if (i === -1) {
                return null;
            }

            let j = i + 1;
            let min = indices[j];
            let minIndex = j;

            while (j < indices.length) {
                if (indices[i] < indices[j] && indices[j] < min) {
                    min = indices[j];
                    minIndex = j;
                }

                ++j;
            }

            swap(indices, i, minIndex);
            ++i;
            j = indices.length - 1;

            while (i < j) {
                swap(indices, i++, j--);
            }

            return loadPermutation(letters, indices);
        }

        function getPermutations(letters) {
            const indices = [];

            // Set the initial indices:
            for (let i = 0; i < letters.length; ++i) {
                indices.push(i);
            }

            const permutations = [];
            permutations.push(letters.split("")); // Initial permutation. 
            let letterPermutation;

            while ((letterPermutation = generateNextPemutation(letters, indices)) !== null) {
                permutations.push(letterPermutation);
            }

            const filter = new Set();

            permutations.forEach((element, index, array) => {
                filter.add(element.join(""));
            });

            return [...filter];
        }

        let ta = Date.now();
        const crOutput = getPermutations(str);
        let tb = Date.now();
        
        console.log("coderodde's version:", (tb - ta), "milliseconds.");
        console.log("coderodde's output", crOutput);
        
        function getRandom(min, max) {
            const minCeiled = Math.ceil(min);
            const maxFloored = Math.floor(max);
            return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
        }

        function factorial(m) {
            let num = 1;
            
            for (let i = 1; i <= m; i++) {
                num = num * i;
            }
            return num;
        }
        
        function check(A, B) {
            for (let i = 0; i < A.length; i++) {
                let a = 0;
                for (let j = 0; j < A[i].length; j++) {

                    if (A[i][j] !== B[j]) {
                        a++;
                    }
                    
                    // Use === instead of ==
                    if (a === B.length) {
                        return false; // <- forgot ;
                    }
                }
            }
            
            return true;
        }

        function getCombinations(A) {
            let x = A.length;
            let B = [];


            while (B.length < factorial(x)) {
                let Y = [];
                for (let i = 0; i < x; i++) {
                    // const instead of let:
                    const a = getRandom(0, (x));
                    
                    if (!Y.includes(A[a])) { // <- !Y.include(A[a]) instead of .. == false!
                        Y.push(A[a]);
                    } else {
//                            i = i - 1;
                        --i; // <- simpler.
                    }
                }

                if (check(B, Y)) {
                    B.push(Y.join(""));
                }
            }
            
            return B;
        }
        
        ta = Date.now();
        const opResult = getCombinations(str);
        tb = Date.now();
        
        console.log("OP's version:", (tb - ta), "milliseconds.");
        console.log("OP's output:", opResult);

    </script>
</body>
</html>

Typical output

I have assembled this demo. It produces the following output:

Code review

I have embedded a couple of advices below:


function check(A, B) {
    for (let i = 0; i < A.length; i++) {
        let a = 0;
        for (let j = 0; j < A[i].length; j++) {

            if (A[i][j] !== B[j]) {
                a++;
            }

            // Use === instead of ==
            if (a === B.length) {
                return false; // <- forgot ;
            }
        }
    }

    return true;
}

function getCombinations(A) {
    let x = A.length;
    let B = [];


    while (B.length < factorial(x)) {
        let Y = [];
        for (let i = 0; i < x; i++) {
            // const instead of let:
            const a = getRandom(0, (x));

            if (!Y.includes(A[a])) { // <- !Y.include(A[a]) instead of .. == false!
                Y.push(A[a]);
            } else {
//                            i = i - 1;
                --i; // <- simpler.
            }
        }

        if (check(B, Y)) {
            B.push(Y.join(""));
        }
    }

    return B;
}

Advices

Advice I (Terminology)

Actually, you are not computing combinations, but rather \$n\$-permutations.

Advices II (Superfluous vertical space)

Occasionally, you use 2 empty lines to separate code. Use 1.

Outro

So why is your random sampling slower? The reason is: towards the end of random generation of yet not visited permutations the probability of getting not yet included sample goes towards \$0\$ and thus involves more and more trials.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.