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:
Or:
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.