开发者

Algorithm for incrementing a String in a non-obvious manner

开发者 https://www.devze.com 2023-03-17 12:30 出处:网络
I want to create random-looking 5 or 6 character alpha-numeric strings, something like: Vg78KY Creating (pseudo-)random Strings has been answered, but I am wondering if there is an algorithm for in

I want to create random-looking 5 or 6 character alpha-numeric strings, something like:

Vg78KY

Creating (pseudo-)random Strings has been answered, but I am wondering if there is an algorithm for incrementing a String in a non-obvious manner. A simple increment of the above String might yield:

Vg78KZ

But I don't want this next String to be guessable, I want开发者_如何学编程 it to look completely different. Of course, successive increments should not yield a previous result as each should be unique.

Any thoughts on how to achieve this much appreciated!

Thanks


An easy approach that avoids the need for lookup tables would be:

  • Increment an integer normally
  • Permute the bits in a non-obvious way (a fixed permutation is probably fine, but if you want something more sophisticated you could use something like George Marsaglia's XORShift algorithm that produces a psuedorandom sequence of integers that only repeats after a very long cycle)
  • Convert to Base64 encoded strings


If we assume there must be a 1:1 mapping from "sequence number" to "random-looking string", then the truncated hash approach will not work as there is no guarantee that the truncated hash won't be subject to collisions.

I'd do something like this:

  • Take the next integer in sequence.
  • Xor with a fixed number.
  • Permute the bits.
  • Encode the number using Base64, Base36, or whatever.

Note that this will be subject to easy analysis by a determined attacker with access to a sufficiently large set of sequence numbers.


What exactly do you mean by increment? If you just want some values that is the result of the original value, the you can use a hash code (possibly a cryptographic hash). Then simply encode it a way that uses the characters you want to use (for example Base64 or something similar) and cut it off at the number of characters you want.

This is a one-way operation, however. That means that you can easily get successor of a value, but can't easily get the predecessor.


import java.util.UUID;
public class RandomStringUUID {

    public static void main(String[] args) {

        UUID uuid = UUID.randomUUID();
        String randomUUIDString = uuid.toString();

        System.out.println("Random UUID String = " + randomUUIDString);
        System.out.println("UUID version       = " + uuid.version());
        System.out.println("UUID variant       = " + uuid.variant());

    }
}

If you want to generate collision safe strings just use UUIDs


If you want it to be incremented it means you have some transformation function F() that transforms from one space to another.

So you probably have a function from {Z} -> {SomeString}

So what you need to do, is just apply the opposite of F() (F-1) to the string, get the original number, increment it, and generate it again.

in pseudocode:

int i = 1;
String s = Transform(i);
int num = UnTransform(s);
num++;
String next = Transform(num);


What about this one:

  1. convert the number to binary format;
  2. change the order of digits by fixed manual mapping (last digit to 6th place, etc);
  3. convert the number back to hash


Another simple way to do this would be:

$hash_key = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
$hash_table = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');

$init = 10000000;
$hash_value = str_replace($hash_key, $hash_table, strval($init));

//$hash_value = 'BAAAAAAA'

//For next value:
$init++;
$hash_value = str_replace($hash_key, $hash_table, strval($init));
//$hash_value = 'BAAAAAAB'

//If you want to increment only the string without knowing the digits:
$prev_hash = $hash_value;
$prev_init = intval(str_replace($hash_table, $hash_key, $prev_hash));
//$prev_init = 10000001

$next_init = $prev_init + 1;
$next_hash = str_replace($hash_key, $hash_table, strval($next_init));
//$next_hash = 'BAAAAAAC'

Hope this helps. :-)


One of possible solutions would be to pre-generate the entire dictionary of all possible strings and then use SecureRandom to point to an index of that dictionary. If a particular element would already be "reserved", you'd simply go to the next available one (this operation can also be pre-generated btw).

The obvious disadvantage of this solution is non-determinism. But this was not requested by OP. And I'm not even sure if determinism is possible in this situation.


Lazy method: keep a hashtable or set to store all existing strings, and each time you generate a random string, check to see if it's in the set. If so, generate a new one until you get one that's not in the set.

This would probably be both memory- and processor-intensive in the long run, though.


You might try and convert the following Python to the language of your choice...

>>> import string, random, itertools
>>> digits = list(string.ascii_lowercase + string.ascii_uppercase + string.digits + '_')
>>> ''.join(digits)
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
>>> digit_count = 4
>>> alldigits = [digits[:] for i in range(digit_count)]
>>> for d in alldigits: random.shuffle(d)

>>> numbergen = (''.join(d) for d in itertools.product(*alldigits))
>>> numbergen.__next__()
'B1xx'
>>> numbergen.__next__()
'B1x1'
>>> numbergen.__next__()
'B1xQ'
>>> numbergen.__next__()
'B1x7'


Well since you want the string to be alphanumeric, then it's pretty straightforward. Create a character array of size 62. This is 26 lowercase letters, 26 uppercase letters, and the 10 digits 0-9. After you fill in the array, loop through N times, where N is the length of your string, selecting a random index each time. So it should look something like this:

   char [] arrayOfCharacters = new char[62];
   int index = 0;
   for(char a = 'a';a<= 'z';a++)
   {
           arrayOfCharacters[index++] = a;
   }//copy this loop for the upper case characters and 0-9
   String randomString = "";
   for(int x = 0;x < N; x++)
   {
           randomString += arrayOfCharacters[(int)(Math.random() * arrayOfCharacters.length)];
   }


That's my code.. it does exactly what you asked for using the UUID to generate a string then execute (-) from it.

import java.util.*;
class KeyGen {
    public static void main(String[] args) {
        String uuid = UUID.randomUUID().toString();
        String str = uuid.replaceAll("[-]", "").toUpperCase();
        String s = "";
        Scanner scan = new Scanner(System.in);
        String[] array = str.split("");
        Random rnd = new Random();
        int N = rnd.nextInt(str.length());
        System.out.println("How many keys you want?");
        int keys = scan.nextInt();
        String[] rndstr = new String[keys];
        System.out.println("How many letters for the first key?");
        int count = scan.nextInt();
        for (int t = 0; t < keys; t++)
        {
            s="";
            count++; 
            for(int i=0; i < count; i++)
                {
                    uuid = UUID.randomUUID().toString();
                    str = uuid.replaceAll("[-]", "").toUpperCase();
                    int len = str.length();
                    N= rnd.nextInt(len) + 1;
                    s = s + array[N]; 
                }
            rndstr[t] = s;
        }
        for (int j=0; j < rndstr.length; j++)
        {
            System.out.println(rndstr[j]);
        } 
}
} 

Simple output:

How many keys you want?
4
How many letters for the first key?
6

Here are your keys:
5F2934A
C8A456A6
B06E49240
FE3AE40CCE


Make your string the result of a hash operation. For example, using your random strings as input:

String input1 = "Vg78KY";
String output1 = String.valueOf(input1.hashCode());

String input2 = "Vg78KZ";
String output2 = String.valueOf(input2.hashCode());

output1 and output2 will be completely different.

0

精彩评论

暂无评论...
验证码 换一张
取 消