Generate IMEI in python
Asked Answered
R

3

5

Hello I am trying to make a function in python to generate valid IMEI numbers so here is my function.The IMEI validation uses the Luhn algorithm so I am trying to implement it in my script.

def getImei():
    num = ''
    suma = 0
    for i in range(0,13):
        digit = random.randrange(0,9)
        suma = suma + digit
        num = num + str(digit)

    suma = suma * 9
    digit = suma % 10
    num = num + str(digit)
    return num

The function however fails to generate valid IMEI numbers. I found an article on wikipedia that tells me how to generate the check digit ( http://en.wikipedia.org/wiki/Luhn_algorithm )

The check digit (x) is obtained by computing the sum of digits then computing 9 times that value modulo 10 (in equation form, (67 * 9 mod 10)). In algorithm form: 1.Compute the sum of the digits (67). 2.Multiply by 9 (603). 3.The last digit, 3, is the check digit.

Am I missing something or the wiki is wrong ?

Ringnecked answered 22/12, 2013 at 19:7 Comment(2)
The algorithm seems fine, but that's only generating a 13-digit number. The IMEI on my phone is 15 digits.Soren
@DanielRoseman it doesn't as it do not generate valid imei (leaving aside number of digits)Allover
A
7

Note that numbers at odd (from the end, starting from 0) postions are doubled, so you have to add it to your code, for example following code will return luhn checksum:

def luhn_residue(digits):
    return sum(sum(divmod(int(d)*(1 + i%2), 10))
                 for i, d in enumerate(digits[::-1])) % 10

Here (1 + i%2) multiplier equals 2 for odd positioned numbers, and 1 for even positioned. Then sum(divmod(..., 10)) returns sum of digits for a (possibly) two digit number, and outer sum sums resulting sequence.

You can use it to generate valid number sequences:

def getImei(N):
    part = ''.join(str(random.randrange(0,9)) for _ in range(N-1))
    res = luhn_residue('{}{}'.format(part, 0))
    return '{}{}'.format(part, -res%10)

Demo:

>>> luhn_residue('79927398713')
0
>>> luhn_residue('05671564547361')
6
>>> luhn_residue(getImei(14))
0
Allover answered 22/12, 2013 at 19:43 Comment(5)
It works, I am python n00b could you shed some light on: in function luhn_residue why is return on the first line and how it gets called in that for loop ? :) ThanksRingnecked
@Ringnecked added some commentsAllover
@Ringnecked code extensively uses list and generator comprehensions, see pythonforbeginners.com/lists/list-comprehensions-in-pythonAllover
This code fails to validate the following IMEI: 357805023984942.Furcula
@Furcula what do you mean by fails to validate? luhn_residue('357805023984942') returns 0Allover
C
1

You're skipping the step where you double every other digit and take the sum of them if the result is bigger than 10. From Wikipedia:

From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if product of this doubling operation is greater than 9 (e.g., 7 * 2 = 14), then sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5).

Craw answered 22/12, 2013 at 19:42 Comment(0)
F
1

The solution from @alko isnot working for all IMEI numbers out there: see this list of valid imei numbers (or cc numbers, which is actually the same).

Here is the solution that works:

def luhn_residue(digits):
    """ Lunh10 residue value """
    s = sum(d if (n % 2 == 1) else (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)[d]
            for n, d in enumerate(map(int, reversed(digits))))
    return (10 - s % 10) % 10
Furcula answered 15/2, 2014 at 2:19 Comment(2)
what way it works? for example luhn_residue('378282246310005') (value provided in your link) it returns 2.Allover
@alko, you're right, will soon provide a working version when it comes to thatFurcula

© 2022 - 2025 — McMap. All rights reserved.