How to write Python code for generating Italian car number plates?

Question:

The three-digit number changes first, then the letters from right to left. So, first plate is AA 000 AA, followed by AA 001 AA…AA 999 AA, then AA 000 AB to AA 999 AZ, then AA 000 BA to AA 999 ZZ, then AB 000 AA to AZ 999 ZZ, then BA 000 AA to ZZ 999 ZZ.

Asked By: Giovanni Casini

||

Answers:

Here’s a reasonably general approach to this problem:

digits = [ 
  # value, init, count, suffix, order 
  [ 'A', 'A', 26 , '',  6], 
  [ 'A', 'A', 26 , ' ', 5], 
  [ '0', '0', 10, '', 2 ], 
  [ '0', '0', 10, '', 1 ], 
  [ '0', '0', 10, ' ', 0 ], 
  [ 'A', 'A', 26 , '',  4], 
  [ 'A', 'A', 26 , '', 3], 
]

def plate(digits):
  out = []
  for item in digits:
    out.append( item[0] )
    out.append( item[3] )

  return ''.join(out)

def increment(digits):
  digits = sorted(digits, key = lambda x : x[-1] )
  for item in digits:
    v =  ord(item[0]) + 1   
    item[0] = chr(v) 
    if v <  ord(item[1]) + item[2]:
      break
    item[0] = item[1]
    
     
    
  

value = plate( digits ) 
while value != 'ZZ 999 ZZ':
  print(value)
  increment(digits)
  value = plate( digits ) 
print(value)

There is a lot of conversions between string and int. I’m also building the output string every time, so it’s somewhat slow.

The output is large. There are about 457 million output values ( 26^4 * 10^3 ).

Answered By: Mark

Just use a couple for loops. But this is a very big output (456_976_000 lines):

from string import ascii_uppercase as letters

for l1 in letters:
    for l2 in letters:
        for l3 in letters:
            for l4 in letters:
                for i in range(1000):
                    print(f'{l1}{l2} {i:03d} {l3}{l4}')
Answered By: Yevhen Kuzmovych

I suggest writing a function that calculates the string of the nth number plate based on number n:

def numberplate(i):
    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # Remove invalid ones
    c = len(chars)
    return chars[i//1000//c**3] + chars[(i//1000%c**3)//c**2] + "{:03d}".format(i%1000) +  chars[(i//1000%c**2)//c] + chars[i//(1000)%c**1]

print(numberplate(0))
print(numberplate(456))
print(numberplate(1000))
print(numberplate(1335))
print(numberplate(1000*26**4-1))

# The below will run for a while...
for i in range(1000*26**4):
    print(numberplate(i))

Edit: I am not using ascii_uppercase on purpose as in many countries, some characters like O and I are not allowed in license plates.

Answered By: treuss

Another solution, an alternative to nested loops, also quite readable.

Use itertools.product() instead of nested loops : "cartesian product, equivalent to a nested for-loop".

Use a generator expression to build the plate string, in order to keep the whole thing "iterable".

from string import ascii_uppercase
from itertools import product

plate_gen = (f"{l1}{l2} {n:03d} {l3}{l4}" for l1, l2, n, l3, l4  in product(ascii_uppercase, ascii_uppercase, range(1000), ascii_uppercase, ascii_uppercase))

Then use the generator, in a "streaming mode", to print or whatever you want.

for plate in plate_gen:
    print(plate)
Answered By: 0x0fba
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.