Aboutcouponcode-飞外

https://stackoverflow.com/questions/11708559/coupon-code-generation/11756222#11756222
Basically you can split your operation into to parts:

Somehow "encrypt" your initial numbern, so that two consecutive numbers yield (very) different resultsConstruct your "human-readable" code from the result of step 1

For step 1 I'd suggest to use a simple block cipher (e.g. aFeistel cipherwith a round function of your choice). See alsothis question.

Feistel ciphers work in severalrounds. During each round, someround functionis applied to one half of the input, the result isxored with the other half and the two halves are swapped. The nice thing about Feistel ciphers is that the round function hasn't to be two-way (the input to the round function is retained unmodified after each round, so the result of the round function can be reconstructed during decryption). Therefore you can choose whatever crazy operation(s) you like :). Also Feistel ciphers are symmetric, which fulfills your first requirement.

A short example in C#

const int BITCOUNT = 30;const int BITMASK = (1 BITCOUNT/2) - 1;static uint roundFunction(uint number) { return (((number ^ 47894) + 25) 1) BITMASK;static uint crypt(uint number) { uint left = number (BITCOUNT/2); uint right = number BITMASK; for (int round = 0; round 10; ++round) { left = left ^ roundFunction(right); uint temp = left; left = right; right = temp; return left | (right (BITCOUNT/2));

(Note that after the last round there is no swapping, in the code the swapping is simply undone in the construction of the result)

Apart from fulfilling your requirements 3 and 4 (the function istotal, so for different inputs you get different outputs and the input is "totally scrambled" according to your informal definition) it is also it's own inverse (thus implicitely fulfilling requirement 1), i.e.crypt(crypt(x))==xfor eachxin the input domain (0..2^30-1in this implementation). Also it's cheap in terms of performance requirements.

For step 2 just encode the result to some base of your choice. For instance, to encode a 30-bit number, you could use 6 "digits" of an alphabet of 32 characters (so you can encode 6*5=30 bits).

An example for this step in C#:

const string ALPHABET= "AG8FOLE2WVTCPY5ZH3NIUDBXSMQK7946";static string couponCode(uint number) { StringBuilder b = new StringBuilder(); for (int i=0; i 6; ++i) { b.Append(ALPHABET[(int)number ((1 5)-1)]); number = number 5; return b.ToString();static uint codeFromCoupon(string coupon) { uint n = 0; for (int i = 0; i 6; ++i) n = n | (((uint)ALPHABET.IndexOf(coupon[i])) (5 * i)); return n;

For inputs 0 - 9 this yields the following coupon codes

0 = 5VZNKB1 = HL766Z2 = TMGSEY3 = P28L4W4 = EM5EWD5 = WIACCZ6 = 8DEPDA7 = OQE33A8 = 4SEQ5A9 = AVAXS5

Note, that this approach has two different internal "secrets": First, the round function together with the number of rounds used and second, the alphabet you use for encoding the encyrpted result. But also note, that the shown implementation is in no way secure in a cryptographical sense!

Also note, that the shown function is atotal bijectivefunction, in the sense, that every possible 6-character code (with characters out of your alphabet) will yield a unique number. To prevent anyone from entering just some random code, you should define some kind of restictions on the input number. E.g. only issue coupons for the first 10.000 numbers. Then, the probability of some random coupon code to be valid would be 10000/2^30=0.00001 (it would require about 50000 attempts to find a correct coupon code). If you need more "security", you can just increase the bit size/coupon code length (see below).

EDIT: Change Coupon code length

Changing the length of the resulting coupon code requires some math: The first (encrypting) step only works on a bit string with even bit count (this is required for the Feistel cipher to work).

In the the second step, the number of bits that can be encoded using a given alphabet depends on the "size" of chosen alphabet and the length of the coupon code. This "entropy", given in bits, is, in general, not an integer number, far less an even integer number. For example:

A 5-digit code using a 30 character alphabet results in 30^5 possible codes which meansld(30^5)=24.53bits/Coupon code.

For a four-digit code, there is a simple solution: Given a 32-Character alphabet you can encode *ld(32^4)=5*4=20* Bits. So you can just set theBITCOUNTto 20 and change theforloop in the second part of the code to run until4(instead of6)

Generating a five-digit code is a bit trickier and somhow "weakens" the algorithm: You can set theBITCOUNTto 24 and just generate a 5-digit code from an alphabet of 30 characters (remove two characters from theALPHABETstring and let theforloop run until5).

But this will not generate all possible 5-digit-codes: with 24 bits you can only get 16,777,216 possible values from the encryption stage, the 5 digit codes could encode 24,300,000 possible numbers, so some possible codes will never be generated. More specifically, the last position of the code will never contain some characters of the alphabet. This can be seen as a drawback, because it narrows down the set of valid codes in an obvious way.

When decoding a coupon code, you'll first have to run thecodeFromCouponfunction and then check, if bit 25 of the result is set. This would mark an invalid code that you can immediately reject. Note that, in practise, this might even be an advantage, since it allows a quick check (e.g. on the client side) of the validity of a code without giving away all internals of the algorithm.

If bit 25 is not set you'll call thecryptfunction and get the original number.