Reputation: 794
I'm having a difficult time coming up with an algorithm that can create short (8 character) referral codes. I want to use an easy to remember pattern where it's not possible to have offensive words show up in the codes. I also don't want any letters getting confused with numbers -- so no 1's, l's, 0's and O's. The pattern I've come up with is aa22aa22. This pattern is basically two alphabetical characters followed by two numerical characters followed by two alphabetical characters followed by two numerical characters. The alphabetical characters are all lower case. This pattern supports over 4 billion possible codes.
Now for the tricky part. I need to store the generated codes in Salesforce. I think this needs to be done in a non-random way because if I do it randomly, I have to check for collisions against already generated codes. This then gets into the governor limits that Salesforce imposes on you. If you're not familiar with governor limits it basically means if you query the database too many times or if your process runs too long the underlying system throws a governor limit error. Random code creation introduces uncertainty regarding how many queries it will take to find a code that doesn't collide with a previously created code. So, now it basically comes down to creating codes that are guaranteed to never repeat and that means creating them sequentially. The problem with a sequential method is the codes are easy to guess.
Yes, I could have a non-Salesforce datastore that could act as the source of record and go with a random method and do the collision check there, but I'd like to see if the worldwide community has any ideas that might work. I have tried to find a weak symmetrical encryption algorithm that can yield 8 character ciphers, but I've had no luck so far.
Upvotes: 5
Views: 976
Reputation: 6915
I would chose an approach like using some random number generator to create a non -repeatable sequence like the one suggested here: https://stackoverflow.com/a/196164/2331592
You have to chose a generator that supports up to [number of possible coupons] combinations. Each time you draw a random number then you got another number that is the index of a possible coupon (--> permutation) that can be directly translated into a coupon (but since your coupons digits have different bases it is not so easy as changing numbers base)
if your code pattern looks like a1a
there a
stands for [abcde]
and 1
for [123]
.
this would make 75 permutations.
00 a1a 15 b1a 30 c1a 45 d1a 60 e1a
01 a1b 16 b1b 31 c1b 46 d1b 61 e1b
02 a1c 17 b1c 32 c1c 47 d1c 62 e1c
03 a1d 18 b1d 33 c1d 48 d1d 63 e1d
04 a1e 19 b1e 34 c1e 49 d1e 64 e1e
05 a2a 20 b2a 35 c2a 50 d2a 65 e2a
06 a2b 21 b2b 36 c2b 51 d2b 66 e2b
07 a2c 22 b2c 37 c2c 52 d2c 67 e2c
08 a2d 23 b2d 38 c2d 53 d2d 68 e2d
09 a2e 24 b2e 39 c2e 54 d2e 69 e2e
10 a3a 25 b3a 40 c3a 55 d3a 70 e3a
11 a3b 26 b3b 41 c3b 56 d3b 71 e3b
12 a3c 27 b3c 42 c3c 57 d3c 72 e3c
13 a3d 28 b3d 43 c3d 58 d3d 73 e3d
14 a3e 29 b3e 44 c3e 59 d3e 74 e3e
chosing a simple LCG based on
x = (a*x + c) mod m
setting
x = 1
(any number can be used)
a = 5
(any large number can be used - just make sure that your arithmetic does not overflow)
c = 0
m = 73
Setting m
to be the next smallest prime to your permutation count to make sure that the generator always creates a valid number although that excludes a number of valid combinations at the end - so this generator will never generates the #73 and #74 and also never the #0 since then always 0
is created
this is the generator output until it loops again:
01: 5 11: 31 21: 17 31: 47 41: 14 51: 43 61: 33 71: 44
02: 25 12: 9 22: 12 32: 16 42: 70 52: 69 62: 19 72: 1
03: 52 13: 45 23: 60 33: 7 43: 58 53: 53 63: 22 --> 01: 5
04: 41 14: 6 24: 8 34: 35 44: 71 54: 46 64: 37
05: 59 15: 30 25: 40 35: 29 45: 63 55: 11 65: 39
06: 3 16: 4 26: 54 36: 72 46: 23 56: 55 66: 49
07: 15 17: 20 27: 51 37: 68 47: 42 57: 56 67: 26
08: 2 18: 27 28: 36 38: 48 48: 64 58: 61 68: 57
09: 10 19: 62 29: 34 39: 21 49: 28 59: 13 69: 66
10: 50 20: 18 30: 24 40: 32 50: 67 60: 65 70: 38
Each time we get a different index. from that index we can look up in the above table the code, but we also can calculate it:
The digits in our code have different values (as in out decimal digit system, where each digit has a value ten times higher than the previous digit)
a
positions have 5 possibilities and 1
has 3 possibilies.
a3, a2, a1
possibilies: 5 3 5
digit value: a3*3*5 a2*5 a1*1
digit value: a3*15 a2*5 a1*1
each of the possibilities has an attached value:
letters a b c d e
value 0 1 2 3 4
numbers 1 2 3
value 0 1 2 <-- this is a bit counter intuitive - but we need to start at 0
e3b --> e≙4 3≙2 b≙1
4*3*5 + 2*5 + 1*1
= 60 + 10 + 1
= 71
code e3b --> #71
the other way around to get the code from an index without requiring a look up table.
Each digit can be calvulated by dividing (integer devision) the value by the digits value and take the modulo of the number of possibilites for this digit
#51
a1 = (51 / 1) mod 5 = 1 ≙ b
a2 = (51 / 5) mod 3 = 1 ≙ 2
a3 = (51 / 15) mod 5 = 3 ≙ d
#51 --> code d2b
As a general hint: i would decouple the representation of the codes from the internal number. The number in the example would be the permutation-index. You only need to know the last issued index, to generate the next one.
How the Index is represented is may be independent from this. in our case the a1a
representation.
Some things to look up:
https://en.wikipedia.org/wiki/Positional_notation for number representations
https://en.wikipedia.org/wiki/Linear_congruential_generator to learn how to generator works and how to parameterize it
Upvotes: 2
Reputation: 25641
Before doing something complex and look into randomness, why not just generate a UUID?
https://en.wikipedia.org/wiki/Universally_unique_identifier
Upvotes: 0