Wie erstelle ich in Java einen eindeutigen alphanumerischen Wert mit 5 Zeichen?

Sep 26 2020

Ich arbeite für ein Bankprojekt und ihre Anforderung besteht darin, für jede Transaktion eine eindeutige Transaktionsreferenz zu generieren. Das Format für UTR ist:

<BankCode> <YYDDD> <5-stellige Sequenz-ID> .

Diese 5-stellige Sequenz-ID kann auch alphanumerisch sein. Die Anzahl der Transaktionen pro Tag kann bis zu 100-200.000 betragen.

Wenn ich eine Oracle-Sequenz verwende, kann ich nur 10K-Werte haben.

Ich habe versucht, einen SecureRandomGenerator zu verwenden , und eine Zeichenfolge mit einer Länge von 200 KB und 5 generiert, aber es wurden ungefähr 30 doppelte Zeichenfolgen generiert.

Unten ist das Code-Snippet, das ich verwendet habe

int leftLimit = 48;
int rightLimit = 122;
int i1=0;
Random random = new SecureRandom();
while (i1<200000) {
    String generatedString = random.ints(leftLimit, rightLimit+1)
                                   .filter(i -> (i<=57||i>=65) && ( i<=90|| i>=97))
                                   .limit(5)
                                   .collect(StringBuilder::new,
                                            StringBuilder::appendCodePoint,
                                            StringBuilder::append)
                                   .toString();
    System.out.println(generatedString);
    i1++;
}

Antworten

OlivierGrégoire Sep 26 2020 at 17:16

Wenn Sie eine pseudozufällige Sequenz wünschen, empfehle ich Ihnen, eine benutzerdefinierte Feistel- Implementierung zu verwenden. Feistel ist als wechselseitiger Mechanismus konzipiert, sodass Sie Feistel durch erneutes Anwenden dekodieren können. i == feistel(feistel(i))Wenn Sie also von 1 nach X wechseln, erhalten Sie alle Zahlen zwischen 1 und X genau einmal. Also keine Kollision.

Grundsätzlich stehen Ihnen 36 Zeichen zur Verfügung. Sie haben also 60.466.176 mögliche Werte, aber Sie möchten nur 200.000 davon. Aber eigentlich ist es uns egal, wie viele Sie wollen, denn Feistel stellt sicher, dass es KEINE Kollisionen gibt.

Sie werden feststellen, dass 60.466.176 in Binärform 0b11100110101010010000000000eine 26-Bit-Zahl ist. 26 ist für den Code nicht sehr freundlich, daher werden wir unseren benutzerdefinierten Feistel-Mapper stattdessen auf 24 Bit umbrechen. Wenn Sie eine Zahl in zwei gerade Teile teilen müssen, besteht jeder Teil aus 12 Bits. Dies dient nur zur Erläuterung der Werte, die im folgenden Code angezeigt werden. Bei anderen Implementierungen handelt es sich um 12 statt 16. Dies 0xFFFist auch die Maske für 12 Bits.

Nun der Algorithmus selbst:

  static int feistel24bits(int value) {
    int l1 = (value >> 12) & 0xFFF;
    int r1 = value & 0xFFF;
    for (int i = 0; i < 3; i++) {
      int key = (int)((((1366 * r1 + 150889) % 714025) / 714025d) * 0xFFF);
      int l2 = r1;
      int r2 = l1 ^ key;
      l1 = l2;
      r1 = r2;
    }
    return (r1 << 12) | l1;
  } 

Im Grunde bedeutet dies, dass Sie, wenn Sie diesem Algorithmus eine Zahl zwischen 0und 16777215(= 2 24 -1) geben, eine eindeutige Pseudozufallszahl erhalten, die in eine 5-stellige Zeichenfolge passen könnte, wenn sie in base-36 geschrieben wird.

Wie bringt man das alles zum Laufen? Nun, es ist sehr einfach:

String nextId() {
  int sequence = (retrieveSequence() + 1) & 0xFFFFFF; // bound to 24 bits
  int nextId = feistel24bits(sequence);
  storeSequence(sequence);
  return intIdToString(nextId);
}
static String intIdToString(int id) {
  String str = Integer.toString(id, 36);
  while(str.length() < 5) { str = "0" + str; }
  return str;
}

Hier ist der vollständige Code, den ich verwendet habe.