-1

Find a number, which is smaller than a given number, and has the same amounts of digit as it, and the sum of digits is the same.

N = int(input())

sume = 0
lik = [int(x) for x in str(N)]
for k in range(len(lik)):
    sume += lik[k]

def get_sum(N):
    global sumd
    sumd = 0
    lis = [int(x) for x in str(N)]
    for k in range(len(lis)):
        sumd += lis[k]
    return sumd
cnt = 0
for i in range(N-1, 10**(len(str(N))-1)-1, -1):
    get_sum(i)
    if sumd == sume:
        print(i)
        break
    else:
        cnt += 1
if cnt == N - 10**(len(str(N))-1):
    print(0)
        

And it's perfectly fine, except that it cannot run big inputs, well, the requirement is number which has 15 DIGITS, and mine can do maximum 7 digits, how do I fix this? I guess that I have to replace loops, as it can't take big ones, but also the case which contain zeros.

Codeer
  • 82
  • 9
  • Please read: [Open letter to students with homework problems](https://softwareengineering.meta.stackexchange.com/questions/6166/open-letter-to-students-with-homework-problems) – Emanuel P Mar 01 '23 at 11:58
  • @EmanuelP What about that? Isn't that for people who *don't* make an attempt, who merely copy&paste the homework text? – Kelly Bundy Mar 01 '23 at 12:02
  • @KellyBundy In this case it's more so he is aware that solutions given here might not be what is intended with this exercise. – Emanuel P Mar 01 '23 at 12:06
  • Side note: `sume` can be done in one loop with `sume = sum([int(x) for x in str(n)])`. – B Remmelzwaal Mar 01 '23 at 12:49
  • 2
    Also, instead of bruteforcing a solution, try to think of the possibilities first: you could sort the digits in ascending order, which could lead to a smaller number in one go. Also, you don't have to check any number greater than N. You could also subtract one from a value in location `n`, and add it to location `n+1` to keep the same sum but have a smaller value (e.g. 123 -> 114). It's not smart to try all the quadrillion possible values when you can cut your search space down to only a fraction of it, which is the main reason this assignment was given I reckon. – B Remmelzwaal Mar 01 '23 at 13:00
  • Does such a number exist for input of `99`? – JonSG Mar 01 '23 at 14:26
  • no it will print 0 – Codeer Mar 01 '23 at 14:36
  • @BRemmelzwaal so I'm supposed to make a loop, which decrease the sum every ten number get decreased and also for that unit right? – Codeer Mar 01 '23 at 14:37

2 Answers2

1

I would apply my approach as such:

def smaller_than_N(N):
    list_N = [x for x in str(N)]
    
    # attempt 1: sort
    # catch: will only work if digits of N are NOT in ascending order
    sorted_N = sorted(list_N)
    # fix leading zeros, only if the number isn't all zeros
    if '0' in sorted_N and sorted_N[-1] != '0':
        first_nonzero = next(i for i, n in enumerate(sorted_N) if n != '0')
        sorted_N[0], sorted_N[first_nonzero] = sorted_N[first_nonzero], sorted_N[0]
    sorted_N = int(''.join(sorted_N))
    
    # attempt 2: decrease and increase
    # catch: will NOT work for edge cases such as 100, 99, etc., because no smaller numbers exist.
    for i, curr_digit in enumerate(list_N[:-1]):
        # if highest significance digit is 1, we cannot decrease it. We can also never decrease 0.
        if (i == 0 and curr_digit == "1") or curr_digit == "0":
            continue
        for j, next_digit in enumerate(list_N[i+1:], i+1):
            if next_digit == "9":
                continue
            # subtract 1 from current, add 1 to next, so new number < N but sum is equal
            list_N[i] = str(int(curr_digit)-1)
            list_N[j] = str(int(next_digit)+1)
            return int(''.join(list_N))
    else:
        print("No smaller number exists.")
        return 0

N = int(input())
print(smaller_than_N(N))

The first attempt takes the simplest approach by putting the numbers in the order which minimizes the size of the number by keeping the sum the same. The second attempt looks for a number greater than 0 (or 1 for the first index) to decrease, and the next digit to increase. If found, the operations get performed and the number returned. If no such number exists, a message is printed and we return 0.

Worst case runtime would be O(n^2), when no number is found.

B Remmelzwaal
  • 1,581
  • 2
  • 4
  • 11
0

If I understand the problem correctly, the way I would proceed conceptually would be to start from the left and look for a digit that was not a 0 to decrement. Having found one, start looking for a digit that is not a 9 to increment. Assuming this is what you are after, the actual sum of digits is something you don't need to track.

given_string_original = input("Enter a number to test: ")
given_string_working = list(given_string_original)

found_a_digit_to_decrement = False
found_a_digit_to_increment = False

for index, digit_string in enumerate(given_string_working):
    if not found_a_digit_to_decrement:
        if (index == 0 and digit_string == "1") or digit_string == "0":
            continue
        found_a_digit_to_decrement = True
        given_string_working[index] = str(int(digit_string) -1)
        continue

    if not found_a_digit_to_increment:
        if digit_string == "9":
            continue
        found_a_digit_to_increment = True
        given_string_working[index] = str(int(digit_string) +1)

given_string_working = "".join(given_string_working)

if found_a_digit_to_decrement and found_a_digit_to_increment:
    print(f"{given_string_original} --> {given_string_working}")
else:
    print(f"{given_string_original} --> -1")

This is not the actual code I would use, but I wrote it for clarity and symmetry so it might be easier to follow.

JonSG
  • 10,542
  • 2
  • 25
  • 36