简体   繁体   中英

Testing the equality of the sum of a digits within a number on python?

for example def f(n):

and I wanna check whether the sum of the numbers within n equal to 100 whether it is in 1s, 2s, 3,s 4s, 5s and so on, depending on the length of n .

f(5050)
>>> True

This tests whether 5 + 0 + 5 + 0 == 100 and whether 50 + 50 ==100 and if any are true, it returns True .

Whether it tests in 1s, 2s 3s 4s and so on, depends on the length of the number. For example a number with a length of 5 can only be tested in 1s.

f(12345)
>>> False

This tests whether 1 + 2 + 3 + 4 + 5 == 100 and only that.

If the length of n was 15, it would test the digits in 1s, 3s and 5s.

and finally one more example:

f(25252525)
>>> True

This would test whether 2+5+2+5+2+5+2+5 == 100 and 25+25+25+25==100 and whether 2525+2525==100 So n , which has a length of 8 would be tested in 1s , 2s , and 4s. It cannot be tested with 3s and 5s because the length of all the digits within the number being summed up must be the same.

I hope I was able to explain what I'm after. Usually I would post what I've tried but I have no idea how to iterate over the digits of a number in such a way

The below approach uses generator to split the integer, and no integer <-> string conversion.

This will likely be the most efficient approach of the ones currently listed.

import math

# Define a generator that will split the integer v into chunks of length cl
# Note: This will return the chunks in "reversed" order.
#   split(1234, 2) => [34, 12]
def split(v, cl):
    while v:
        (v,c) = divmod(v, 10**cl)
        yield c


def f(v):
    # Determine the number of digits in v
    n = int(math.log10(v)) + 1
    m = int(math.sqrt(v))
    # Check all chunk lengths in [1, sqrt(v)]
    for cl in range(m):
        # Skip the chunk lengths that would result in unequal chunk sizes 
        if n % (cl+1): continue
        # Create a generator, to split the value v into chunks of length cl
        chunks = split(v, cl+1)
        # If the sum of the chunks is 100, print a message and return True
        if sum(chunks) == 100:
            print("sum = 100 with chunklength: %d" % cl)
            return True
    # If we get here, none of the chunk lengths result in a sum of 100, return False
    return False

print(f(5050))      # True (cl=2)
print("---")
print(f(12345))     # False
print("---")
print(f(25252525))  # True (cl=2)
print("---")

Output:

sum = 100 with chunklength: 2
True
---
False
---
sum = 100 with chunklength: 2
True
---

Without comments and debugging print :

import math

def split(v, cl):
    while v:
        (v,c) = divmod(v, 10**cl)
        yield c

def f(v):
    n = int(math.log10(v)) + 1
    m = int(math.sqrt(v))
    for cl in range(m):
        if n % (cl+1): continue
        if sum(split(v, cl+1)) == 100: return True
    return False

print(f(5050))      # True
print(f(12345))     # False
print(f(25252525))  # True

Assuming that the numbers are always positive integers, you can just divmod() them by 10 until you get to zero:

def int_iter(number):
    while number > 0:
        number, last_digit = divmod(number, 10)
        yield last_digit

Note that gives you the digits in reverse order. That doesn't matter if you're just adding them together, though.

You can pass this around or use it in a for loop, like any other iterable:

digit_sum = sum(int_iter(number))

If you really need a sequence, just pass it to list() :

digit_list = list(int_iter(number))

And if you need them in most-significant-first order, pass it to reversed() :

digits_msf = reversed(list(int_iter(number)))

EDIT:

Whoops, I missed…about half of the question. Things are rather more complicated. You'll need a function to get all the factors of a number—I'm sure there are plenty, so I'll leave that as an excercise for you. Let's assume there's a function factors(number) that returns an iterable of all a number's factors (including nonprimes and 1, but not number itself). We'll also use the int_iter() from my original answer, an int_from_digits() that takes a list of digits and returns a single integer (sort of like the inverse of int_iter() ), and something called grouper() from the itertools recipes .

from itertools import zip_longest

def int_from_digits(digits):
    "Generate an integer from an iterable of single decimal digits"
    # int_from_digits([4, 0, 2, 8, 9]) --> 40289
    # int_from_digits([]) --> 0
    number = 0
    for digit in digits:
        number *= 10
        number += digit
    return number

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

def digit_subsequences(number):
    digits = list(reversed(list(int_iter(number))))
    for factor in factors(number):
        for digit_grouping in grouper(digits, factor):
            yield int_from_digits(digit_grouping)

Finally, armed with all these tools (or rather, one tool and its dependencies), we can perform your check with a simple comprehension and a call to any() :

any(digit_subsequence == 100 for digit_subsequence in digit_subsequences(number))

One possible way, separated into functions for each logical step :

  1. Get factors of n :

def get_factors(n):    
    return set(reduce(list.__add__, 
                ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))
  1. For each l factor of n , split str(n) into chunks of length l :

def get_chunks(str_n, chunk_size):
    total_size = len(str_n)
    return [int(str_n[i:i+chunk_size]) for i in range(0, total_size, chunk_size)]
  1. Check if sum of chunks in step 2 equals 100.

def f(n):
    factors = get_factors(n)
    for l in factors:
        if sum(get_chunks(str(n), l)) == 100:
            return True
    return False

I think this does the trick. Not sure though:

def f(n):
    s = str(n)
    l = len(s)
    for n in (n for n in range(1, l + 1) if l % n == 0):
        ints = [int(''.join(x)) for x in zip(*[iter(s)]*n)]
        if sum(ints) == 100:
            return True
    return False

The zip thing comes from here . It's a little weird but it allows me to split the string up into n-length segments and then join it back together so I can apply an int mapping and then a sum reduction.

The generator just gets all positive divisors of l from 1 to l , both inclusive. There may be faster ways of doing it for large n using math.sqrt and divmod .

def factors(num):
    yield 1
    for i in range(2, num - 1):
        if num % i == 0:
            yield i


def pieces(string, sz):
    for i in range(0, len(string), sz):
        yield string[i:i+sz]


def check_sum(string):
    for f in factors(len(string)):
        pcs = pieces(string, f)
        if sum([int(x) for x in pcs]) == 100:
            print 'True'
            return
    print 'False'

>>> check_sum('5050')
True
>>> check_sum('25252525')
True
>>> check_sum('12345')
False

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