셀프넘버 (self-number)

어떤 자연수 n이 있을 때, d(n)을 n의 각 자릿수 숫자들과 n 자신을 더한 숫자라고 정의하자.

예를 들어

d(91) = 9 + 1 + 91 = 101

이 때, n을 d(n)의 제네레이터(generator)라고 한다. 위의 예에서 91은 101의 제네레이터이다.

어떤 숫자들은 하나 이상의 제네레이터를 가지고 있는데, 101의 제네레이터는 91 뿐 아니라 100도 있다. 그런데 반대로, 제네레이터가 없는 숫자들도 있으며, 이런 숫자를 인도의 수학자 Kaprekar가 셀프 넘버(self-number)라 이름 붙였다. 예를 들어 1,3,5,7,9,20,31 은 셀프 넘버 들이다.

1 이상이고 5000 보다 작은 모든 셀프 넘버들의 합을 구하라.

박응용 3401

2020년 7월 7일 3:22 오후

목록으로
15개의 답변이 있습니다. 1 / 2 Page
def find_gen(number):
    generator = []
    for i in range(1, number):
        count=0
        for n in str(i):
            count += int(n)
        count += i
        if count == number:
            generator.append(i)
    return generator
selfnumber = []
for i in range(1,5001):
    generator = find_gen(i)
    if not generator:
        selfnumber.append(i)
print('셀프넘버들')
print(selfnumber)
print('합')
print(sum(selfnumber))

답은 나옵니다만 더 효율적인 방법이 있으면 가차없이 지적해 주시면 좋겠습니다ㅠ
실력을 키우고 싶어요

김영준

2020년 7월 8일 6:03 오후

먼저 d(n)을 나타내는 람다를 만듭니다. 리스트 컴프리헨션으로 n을 문자열로 변환해서 자리수를 나타내는 문자를 하나씩 얻어서 각각 숫자로 바꾼 리스트를 만들고, n이 있는 리스트를 만들어 두 리스트를 합친 다음, 합을 구합니다.

>>> d = lambda n: sum([int(c) for c in str(n)] + [n])
>>> d(91)
101

다음은 n이 셀프 넘버인지 확인하는 함수입니다. n의 제네레이터 리스트를 만들고, 그 길이가 0이면 n이 셀프 넘버가 맞으므로 True를 반환합니다.

def self_number(n):
    return len([i for i in range(n) if d(i) == n]) == 0

self_number가 잘 작동하는지 테스트해봅니다.

>>> self_number(1)
True
>>> self_number(2)
False
>>> self_number(3)
True
...

이제 5000까지의 합을 구해봅니다. 좀 오래 걸리네요.^^

>>> sum([i for i in range(5000) if self_number(i)])
1227365

최용

M 2020년 7월 10일 10:37 오후

+1 최용님, 환영합니다. 상세한 설명을 곁들인 풀이 감사합니다. - 박응용님, 2020년 7월 10일 10:43 오후 추천 , 대댓글

제가 지난번에 푼 것이 너무 오래 걸려서 불만이었는데, Fast_Learner 님 풀이를 보고 힌트를 얻었습니다.

d는 지난번에 제가 한 것과 같습니다.

>>> d = lambda n: sum([int(c) for c in str(n)] + [n])
>>> d(91)  # 91은 101의 제네레이터
101

제네레이터의 딕셔너리 G를 만듭니다. 딕셔너리 컴프리헨션을 이용했습니다.

>>> G = {n: d(n) for n in range(1, 5001)}
>>> G[91]    # 91은 101의 제네레이터
101
>>> G[100]  # 100도 101의 제네레이터
101

셀프 넘버의 리스트 S를 만들고 합을 구합니다.

>>> S = [i for i in range(1, 5001) if i not in G.values()]

리스트 대신 세트(set)를 만들 수도 있습니다.

>>> S = set(range(1, 5001)) - set(G.values())

S의 모든 원소의 합을 구합니다.

>>> sum(S)
1227365

최용

M 2020년 7월 16일 7:41 오후

1,227,365

chadolskii

2020년 7월 8일 2:23 오후

고생하셨습니다. 풀이도 함께 올려주시면 좋을 것 같습니다. - 박응용님, 2020년 7월 8일 2:37 오후 추천 , 대댓글
sum_of_element=[]
sum_of_all=[]
## init
limit_sum=5000
for i in range(1,limit_sum):
    sum_of_all.append(i)


def check_value(t):
    if (t) > 0 and (t) <= 9 :
        sum_of_element.append(t)  

def remove_sum(to_remove):
    try:
        sum_of_all.remove(to_remove)
    except:
        pass

for i in range(1,limit_sum):
    #
    t0=i%10
    check_value(t0)
    t1=(i//10%10)
    check_value(t1)
    t2=(i//100%10)
    check_value(t2)
    t3=(i//1000%10)
    check_value(t3)
    sum_of_element.append(i)
    #
    remove_sum(sum(sum_of_element))
    del sum_of_element[0:]

#print (sum_of_all)
print (sum(sum_of_all))

Python internal function이 익숙치 않아서 풀이가 ..

johlim

M 2020년 7월 8일 7:03 오후

def d(n):
    digit_sum = sum([int(i) for i in str(n)])
    return n + digit_sum

nums = {i for i in range(5000)}
nums_gen_exist = set()
for i in range(1,5000):
    if d(i) < 5000:
        nums_gen_exist.add(d(i))

nums_gen_not_exist = nums.difference(nums_gen_exist)

print(sum(nums_gen_not_exist))

wjpark11

M 2020년 7월 15일 3:50 오후

n = 0
not_self_number = []
self_number = []
while n < 5001:
    if n < 10:
        x = n + n
    elif n < 100:
        m = str(n)
        n1 = int(m[0])
        n2 = int(m[1])
        x = n + n1 + n2
    elif n < 1000:
        m = str(n)
        n1 = int(m[0])
        n2 = int(m[1])
        n3 = int(m[2])
        x = n + n1 + n2 + n3
    elif n < 5000:
        m = str(n)
        n1 = int(m[0])
        n2 = int(m[1])
        n3 = int(m[2])
        n4 = int(m[3])
        x = n + n1 + n2 + n3 + n4
    else:
        pass
    not_self_number.append(x)
    n += 1
for i in range(1,5001):
    if i not in not_self_number:
        self_number.append(i)
    else:
        pass
print(sum(self_number))

Fast_Learner

M 2020년 7월 16일 2:06 오후

a = set()
b = set(range(1, 5000))

for i in range(1, 5000):
    for j in str(i):
        i = i + int(j)

    if i < 5000:
        a.add(i)

b = b - a

print(sum(b))

>>>1227365

iirtrtrt

M 2020년 8월 2일 8:24 오후

d = (lambda num: num+sum(int(i) for i in str(num)))
numlist = list(range(1,5001))
for num in range(1,5001):
    try:
        numlist.remove(d(num))
    except:
        ...
print(sum(numlist))

파이썬을 연습하기 좋은 퀴즈인 것 같습니다!!
댓글을 보고 힌트를 얻어 조금 더 짧게 만들어보려고 했는데 아직 초보수준이라 아쉽네요 ㅎㅎ
많이 배우고 갑니다 ㅎㅎ

9iant

M 2020년 8월 12일 9:38 오전

파이썬을 시작한지 얼마 되지않아 많이 미숙하지만, 그래도 해보았습니다.

저는 셀프넘버 들을 구하지 않고 셀프넘버들의 합 만 구했습니다.

def d(a) :
    result = int(a/1000) + int(a/100)%10 + int(a/10)%10 + a%10 + a
    return result

이 함수는 제네레이터를 구하는 함수입니다.
참고로 말하자면 int(a/1000)은 천의자리, int(a/100)%10은 백의자리, int(a/10)%10은 십의자리, a%10은 일의자리를 나타낸겁니다.
왜 만들었는지는 밑에 코드를 보면 아실수 있습니다.

result = 0
gen_number = list(set([d(i) for i in range(1,5000) if d(i) < 5000]))

위에 result는 뒤에 쓸 용도로 0으로 먼저 지정해주었습니다.
gen_number 변수는 d(i)가 5000이 넘지 않을때 d(i) (여기서 i는 1~4999)의 값들을 모두 구한것
에서 set을 취해주어 중복되는 값들을 제거해주고 다시 list를 취해주어 리스트로 변환해주었습니다.
따라서 이 리스트에는 중복되지 않은 제네레이터 밖에 없습니다.

for i in range(0,len(gen_number)) :
    result += gen_number[i]

위에 for문은 아까 선언해주었던 result 변수에다가 gen_number 리스트에있는 모든수를 더해 주기위해 썼습니다.
그렇게 된다면 제네레이터만 전부 더한것이 되고, 반대로 말하자면 셀프넘버 만 더하지않은것이 됩니다.
따라서 전체에서 이 수를 빼면 셀프넘버만 더한것의 값이 나옵니다.

all = 0
for i in range(1,5000) :
    all += i

위에 for문은 1부터 4999까지의 합을 all에 지정해두기 위해 썼습니다.

result = all - result

위에서 언급한 내용대로 셀프넘버의 합을 구하기 위해 썼습니다.

print(result)

많이 미숙하지만 봐주셔서 감사합니다. 피드백은 환영입니다.

송고쟝

M 2020년 8월 25일 9:31 오전

+1 작성하신 코드에 마크다운을 적용했어요. - 박응용님, M 2020년 8월 25일 7:51 오후 추천 , 대댓글