3

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

JSON C11
  • 11,272
  • 7
  • 78
  • 65
Roger
  • 107
  • 7
  • hint: `x = str(my_number)`, and now you can inspect the individual digits of `my_number` by indexing `x`. – Kevin Apr 11 '15 at 02:08
  • But I thought indexing wouldn't work because n can be any number with any length – Roger Apr 11 '15 at 02:10
  • so what is `f(343)`? Or is your function only supposed to handle positive integers with an even number of digits? – Shashank Apr 11 '15 at 02:10
  • Strings can be any length too. I don't see a problem there. – Kevin Apr 11 '15 at 02:11
  • it would be False. 3 + 4 + 3 = 10 and not 100 – Roger Apr 11 '15 at 02:11
  • Then `f(34343)`? :) My question is how to "split" numbers with odd number of digits. – Shashank Apr 11 '15 at 02:11
  • The part with "whether 5 + 0 + 5 + 0 == 100 and whether 50 + 50 ==100 and if any are true" changes this from a tidy problem into a messy problem, by the way. Are you sure it has to look for all combinations? – TigerhawkT3 Apr 11 '15 at 02:12
  • in any way that the the numbers being summed up are all the same length. That would be 3 + 4 + 3 + 4 + 3. Look at my example of a number with a length of 15 – Roger Apr 11 '15 at 02:13
  • so `f(100)` returns True? Just clarifying some stuff – Shashank Apr 11 '15 at 02:15
  • @Shashank , something like f(999999999991) would also be true when tested with 1s. This is why different combinations are required – Roger Apr 11 '15 at 02:16
  • Is this for use in something, or simply a programming exercise? – TigerhawkT3 Apr 11 '15 at 02:21

5 Answers5

2

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
jedwards
  • 29,432
  • 3
  • 65
  • 92
  • Your names should really be longer and more descriptive, and I disagree with onelining those `if` statements and checking against `100` within `f()` (seems like something that should go outside)… That said, your answer is the simplest currently on here and requires only the standard `math` module. Nice job. +1 – Blacklight Shining Apr 11 '15 at 14:51
  • @BlacklightShining I understand that it may be confusing, but the variables names weren't really arbitrary letters as they may seem. `f()` was taken from the OP's code, `v` is the **v**alue, `c` is the **c**hunk, `cl` is the **c**hunk **l**ength, `n` is the **n**umber of digits, `m` is the **m**ax value of the for loop. As for in-lining, I have mixed feelings about it in general. My reaction to them fall somewhere on a "this makes perfect sense" to "this is crazy" spectrum. In the case of simple blocks (`continue`, `return True`), it usually falls in the "this isn't unreasonable" range. – jedwards Apr 11 '15 at 18:02
1

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))
Blacklight Shining
  • 1,468
  • 2
  • 11
  • 28
1

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
Community
  • 1
  • 1
har07
  • 88,338
  • 12
  • 84
  • 137
1

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.

Shashank
  • 13,713
  • 5
  • 37
  • 63
  • Thanks for the awesome reply. This code seems to be working really well. Just one more thing, is there a way to have the digits, which are being added separately, squared separately? For example it checks for f(5050) checks for 50^2 + 50^2 instead of 50+50 – Roger Apr 11 '15 at 03:36
  • @Roger Sure. Just do `map(lambda x: x**2, ints)` to get a a list (or iterable in Python 3) of all the squared values. Then cast `sum()` on that expression. :) Alternatively you can do `[x**2 for x in ints]` That's a list comprehension and basically it does the same thing as `map` (except its a bit faster and easier to read). – Shashank Apr 11 '15 at 03:49
1
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
Saksham Varma
  • 2,122
  • 13
  • 15