So, what I pretty much want to do, is, make a "function"? (I think that's what I'll call it) that makes up every possible combination of alphanumeric characters for the given length.
I've tried a countless number of things, my brain is really confused right now...
Here's one of the closest things I tried..
01 | --Set up the character table |
02 | local chars, charStr = { } , "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
03 | for i = 1 ,#charStr do |
04 | table.insert(chars, charStr:sub(i,i)) |
05 | end |
06 |
07 | setmetatable (chars, { -- make the table loop (magic) |
08 | __index = function (t, i) |
09 | return (i>#t and t [ i-#t ] ) or (#t>i and t [ #t-i ] ) |
10 | end |
11 | } ) |
12 |
13 | --Continue |
14 | local maxLen = 3 |
15 | local s = "" |
You can't do this with just for
loops (unless you know specifically how long it will always be -- e.g., 5)
The most straightforward way to do this is recursion.
Let's call a n
-string a string made of the alphabet that is n
characters long.
Then we know all of the 1
-strings -- they are just all of the letters in chars
.
What are all of the 2
-strings? Well, they're any two letters in chars
. But we can say something a little nicer:
A 2
-string is any one character from chars
followed by any of the 1
-strings.
We can use this definition in general:
A
k
-string is any one character fromchars
followed by ak-1
-string
The code to get all of them, then, would look something like this:
01 | chars = "abcd" |
02 |
03 | -- Returns a list of strings (each of length k) |
04 | -- (Works inductively) |
05 | function kstrings( k ) |
06 | if k = = 0 then |
07 | -- There is only one length 0 string: |
08 | return { "" } |
09 | else |
10 | local kminus 1 s = kstrings( k - 1 ) -- all (k-1)-strings |
11 | local result = { } |
12 | for i = 1 , #chars do |
13 | -- chars[i] followed by each (k-1)-string |
14 | for _, km 1 in pairs (kminus 1 s) do |
15 | table.insert(result, chars:sub(i, i) .. km 1 ) |
Warning: This will be very, very slow. The result will be #chars^k
strings. If you're using uppercase and lowercase and you're just inputting 5, that's 380 million. Each will be 5 chars long, so this will take at least 1.9 GB to store1.
Because of the massive memory requirement, you probably won't be able to generate a list of all of them.
Instead, we can make a "successor" of a string by counting up repeatedly in base-#chars
.
01 | function successor(list, base, at) |
02 | at = at or #list; |
03 | list [ at ] = list [ at ] + 1 |
04 | if list [ at ] > base then |
05 | list [ at ] = 1 |
06 | if at > 1 then |
07 | return successor(list, base, at - 1 ); |
08 | end |
09 | else |
10 | return true ; |
11 | end |
12 | end |
13 |
14 | function numsToString(list, char) |
15 | local r = "" |
This will still have to consider a very large number of strings, but it does it in much less memory, so this will actually be able to be usable (just extremely slow)
Actually, it's might not be that bad, as long as you're really using strings. But it will still be really bad (and the less memory use will come at a high performance cost) ↩
Note: This is a difficult question to compute. Any answer is probably going to be pretty slow, because you will need to do many calculations regardless of what approach you use. Beware.
Probably the most intuitive answer to this problem is to go through all permutations and then eliminate permutations that aren't unique combinations.
The first thing we want to do is get all permutations of a set of elements. My favorite algorithm to use is the QuickPerm algorithm. Below is an implementation of QuickPerm with a function parameter execute that executes every time QuickPerm calculates a new permutation. This will be useful for solving our problem, because then we just have to eliminate permutations that aren't unique combinations.
01 | function executeOverPermutations(array, execute) |
02 | -- QuickPerm algorithm implementation |
03 |
04 | local function getArrayOfZeroes(size) |
05 | local array = { } |
06 | for index = 0 , size - 1 do |
07 | array [ index ] = index |
08 | end |
09 | return array |
10 | end |
11 |
12 | local size = #array |
13 |
14 | local p = getArrayOfZeroes(size + 1 ) |
15 |
One of the best ways to check if two permutations are the same combination is comparing the two when they are ordered. For example...
01 | function areArrayElementsSame(a, b) |
02 | for index = 1 , #a do |
03 | if a [ index ] ~ = b [ index ] then |
04 | return false |
05 | end |
06 | end |
07 | return true |
08 | end |
09 |
10 | local a = { 1 , 2 , 3 } |
11 | local b = { 3 , 1 , 2 } |
12 | table.sort(a) -- orders a |
13 | table.sort(b) -- orders b |
14 | print (areArrayElementsSame(a, b)) |
For this problem we will use table.sort because its comparison algorithm is good enough to differentiate between capital letters, lowercase letters, and numbers. We will first sort the permutation (which should be in an array) and then check if it already exists within a lookup table that we have created. If it does not exist in there yet, we just add it to the lookup table.
1 | local combinations = { } |
2 | function addTableToCombination(combination) |
3 | table.sort(combination) |
4 | local uniqueCombination = table.concat(combination) |
5 | if not combinations [ uniqueCombination ] then |
6 | combinations [ uniqueCombination ] = true |
7 | print (uniqueCombination) |
8 | end |
9 | end |
Putting all of this together, we get an answer...
01 | local maxLength = 3 |
02 | local source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" |
03 |
04 | local combinations = { } |
05 |
06 | function getArrayFromString(str) |
07 | local array = { } |
08 | for strIndex = 1 , #str do |
09 | local character = str:sub(strIndex, strIndex) |
10 | array [ strIndex ] = character |
11 | end |
12 | return array |
13 | end |
14 |
15 | function addTableToCombination(combination) |
Hello me, I managed to answer myself, here's what I used. I realised I need to use recursion. BlueTaslem's way is way simpler, go use that.
01 | function stringCombinations(chars, maxLen) |
02 | local TO_RETURN, a = { } , { } ; |
03 | local vals = { } ; |
04 |
05 | for i = 1 , #chars do |
06 | table.insert(vals, chars:sub(i,i)); |
07 | end |
08 |
09 | local function aux(m) -- you need recursion |
10 | for i = 1 , #vals do |
11 | a [ m ] = vals [ i ] ; |
12 | if m < = maxLen then |
13 | aux(m + 1 ); |
14 | else |
15 | local s = table.concat(a); |