简体   繁体   中英

Python sequential number generator with 0-9 and A-Z

I'm trying to accomplish a number generator which increments numbers (0-9) and characters (AZ not case-sensitive) for unique identification of some object in our software project. The output string should have a length of 14.

The generator should be able to create a range of sequential numbers/characters (see examples below) which will be used on potential hundres of devices running some python applications with databases. We would like to implement following logic and 'provide' these sequential number ranges to the devices so we make sure we have unique ids across all systems. We would prefer this logic instead of classic random id creation.

Increment should start from right to left. Numbers start with 0-9. If 9 is reached, the index should then increment from AZ. If Z is reached, the left index number should be incremented from 0 to 1 or 9 => AZ(one higher). The right index should then again be incremented by the same logic starting by 0 => 1-9 and afterwards AZ.

A few example outputs:

  • 00000000000009
  • 0000000000000A
  • 0000000000000B

Or:

  • 0000000000000Z
  • 00000000000010
  • 00000000000011

And so on.

The function should also be able to start at a given number, for eg 00000000005X which then continues with 00000000005Y etc..

How can I build this in Python?

You can use constants from the string module to get all the digits / uppercase ASCII characters, and then use itertools.product to generate the IDs:

import string
import itertools

# Create the iterable.
characters = string.digits + string.ascii_uppercase
n = 14
id_iterable = map(''.join, itertools.product(characters, repeat=n))

# Get the first 50 elements in the iterable and print them out (for demo purposes).
head = list(itertools.islice(id_iterable, 50))
print(*head, sep='\n')

This outputs:

00000000000000
00000000000001
00000000000002
00000000000003
00000000000004
00000000000005
00000000000006
00000000000007
00000000000008
00000000000009
0000000000000A
0000000000000B
0000000000000C
0000000000000D
0000000000000E
0000000000000F
0000000000000G
0000000000000H
0000000000000I
0000000000000J
0000000000000K
0000000000000L
0000000000000M
0000000000000N
0000000000000O
0000000000000P
0000000000000Q
0000000000000R
0000000000000S
0000000000000T
0000000000000U
0000000000000V
0000000000000W
0000000000000X
0000000000000Y
0000000000000Z
00000000000010
00000000000011
00000000000012
00000000000013
00000000000014
00000000000015
00000000000016
00000000000017
00000000000018
00000000000019
0000000000001A
0000000000001B
0000000000001C
0000000000001D

You can try:

import numpy as np

def f(start):
    n = int(start, base=36)
    while True:
        yield np.base_repr(n, 36).rjust(14,"0")
        n += 1

Then:

g = f('0000000000012S')

for i in range(10):
    print(next(g))

It gives:

0000000000012S
0000000000012T
0000000000012U
0000000000012V
0000000000012W
0000000000012X
0000000000012Y
0000000000012Z
00000000000130
00000000000131

This program only uses the string module, and not itertools I also added constants called LENGTH and START which you can change

Code:

from string import ascii_uppercase

sequence = [str(i) for i in range(10)] + list(ascii_uppercase)

LENGTH = 14
START = '0' * LENGTH

last = list(START)
while last != list('Z' * LENGTH):
    print(''.join(last))
    i = 1
    while last[-i] == 'Z':
        last[-i] = '0'
        i += 1
    last = last[:-i] + [sequence[sequence.index(last[-i]) + 1]] + last[LENGTH-i+1:]

Output:

00000000000000 
00000000000001 
00000000000002 
00000000000003 
00000000000004 
00000000000005 
00000000000006 
00000000000007 
00000000000008 
00000000000009 
0000000000000A 
0000000000000B 
0000000000000C 
0000000000000D 
0000000000000E 
0000000000000F 
0000000000000G 
0000000000000H 
0000000000000I 
0000000000000J 
0000000000000K 
0000000000000L 
0000000000000M 
0000000000000N 
0000000000000O 
0000000000000P 
0000000000000Q 
0000000000000R 
0000000000000S 
0000000000000T 
0000000000000U 
0000000000000V 
0000000000000W 
0000000000000X 
0000000000000Y 
0000000000000Z 
00000000000010 
00000000000011 
00000000000012 
00000000000013 
00000000000014 
00000000000015 
00000000000016 
00000000000017 
00000000000018 
00000000000019 
0000000000001A 
0000000000001B 
0000000000001C 
0000000000001D 
0000000000001E 
0000000000001F 
0000000000001G
...

Was using your question as an exercise to see if I could quickly implement the algorithm in JS, wasn't intending on providing the solution, as I expected this "homework-style" question would get answers pretty quickly. @bb1's numpy solution seems quite good, as does the itertools approach (though it's not actually written as a generator function). Though both have library overheads.

Was a bit surprised there wasn't a good algorithmic approach, just library answers. So figured I'd share mine even though it was in the wrong language. Adopting this for Python shouldn't be too tricky, outside of maybe converting the JS closure to a generator function in Python. As this is written, if we happen to hit "ZZZZZZZZZZZZZZ" the next value generated is "00000000000000" and we restart.

function GetGenerator(start_val) {
    
    const min_val = "0"
    const id_len = 14;
    
    if (start_val && start_val.length != id_len) 
        throw "Initial value length isn't " + id_len
    
    // A dictionary mapping each character to the next & 'Z' to 'undefined'
    const incrementer = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")
        .reduce((acc, cur, idx, arr) => (acc[cur] = arr[idx+1], acc), {})
    
    const counter = start_val ? start_val.split("") : Array(id_len).fill(min_val)
    
    function increment() {
        //  Moving backwards through the array  <--
        // ["0", "0", "0", "0", ... , "0", "5", "X" ]
        for(let i = id_len - 1; i >= 0; i--) {
            const next = incrementer[counter[i]]
            if (next) {
                counter[i] = next
                return
            } else {
                counter[i] = min_val
                // No return, it's a carry. Increment the next index.
            }                
        }
    }
    
    return function gen() {
        increment()
        return counter.join("")
    }
}

Used like:

const generator = GetGenerator("0000000000005X")

generator()
>>> "0000000000005Y"
generator()
>>> "0000000000005Z"
generator()
>>> "00000000000060"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM