Path: csiph.com!usenet.pasdenom.info!nntpfeed.proxad.net!proxad.net!feeder1-1.proxad.net!ecngs!feeder2.ecngs.de!novso.com!newsfeed.xs4all.nl!newsfeed4a.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.015 X-Spam-Evidence: '*H*': 0.97; '*S*': 0.00; '"""': 0.07; 'cache': 0.07; 'squares': 0.07; 'comment,': 0.09; 'function:': 0.09; 'subject:number': 0.09; 'def': 0.12; '11:59': 0.16; 'bugs.': 0.16; 'cache:': 0.16; 'cached': 0.16; 'readability': 0.16; 'seconds.': 0.16; 'set()': 0.16; 'sooner.': 0.16; 'subject:happy': 0.16; 'think.': 0.16; 'true:': 0.16; 'unhappy': 0.16; 'zeroes': 0.16; 'wrote:': 0.18; 'version.': 0.19; 'not,': 0.20; 'memory': 0.22; 'coding': 0.22; 'header:User-Agent:1': 0.23; 'finally,': 0.24; 'decide': 0.24; "haven't": 0.24; 'looks': 0.24; 'sort': 0.25; 'compare': 0.26; 'values': 0.27; 'header:In-Reply-To:1': 0.27; 'tried': 0.27; 'testing': 0.29; 'am,': 0.29; 'besides': 0.30; 'sets': 0.30; 'url:wiki': 0.31; 'subject:skip:i 10': 0.31; 'url:wikipedia': 0.31; 'skip:c 30': 0.32; 'implemented': 0.33; 'skip:# 10': 0.33; 'skip:_ 10': 0.34; 'could': 0.34; 'good.': 0.35; 'one,': 0.35; 'but': 0.35; 'there': 0.35; 'false': 0.36; "didn't": 0.36; 'possible': 0.36; 'url:org': 0.36; 'should': 0.36; 'members.': 0.37; 'mine': 0.38; 'version,': 0.38; 'to:addr:python- list': 0.38; 'little': 0.38; 'bad': 0.39; 'sure': 0.39; 'to:addr:python.org': 0.39; 'break': 0.61; 'introduced': 0.61; "you're": 0.61; 'first': 0.61; 'show': 0.63; 'real': 0.63; 'more': 0.64; 'charset:windows-1252': 0.65; 'between': 0.67; 'optimized': 0.68; 'reads': 0.68; 'received:74.208': 0.68; 'results': 0.69; '10%': 0.74; 'counts': 0.83; 'sets,': 0.84; 'yours': 0.88; 'items,': 0.91; '280': 0.93 Date: Thu, 30 Apr 2015 14:53:10 -0400 From: Dave Angel User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.6.0 MIME-Version: 1.0 To: python-list@python.org Subject: Re: Is my implementation of happy number OK References: <87oam5vc8k.fsf@Equus.decebal.nl> In-Reply-To: <87oam5vc8k.fsf@Equus.decebal.nl> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit X-Provags-ID: V03:K0:OZSYiZBhUM0lUL5v4zZ2HrP/sgv4BHt3F9/CKtb/5IOwfwLgEz3 blVuvuit9C9qsjYf8FXKLHesUqtoHyEDjuNy81Iv0pocY+/SyOsb5mMWLUS8MozLzlMRwQi hNfDTBgOfoWElUtqfRzPf+8cxpL4DN/jtWsa79i7gIupXln/yOZX5sCRQDhKZM9ZEboFxp7 er2v3JgkpeOtergpk6DqQ== X-UI-Out-Filterresults: notjunk:1; X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 103 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1430420006 news.xs4all.nl 2884 [2001:888:2000:d::a6]:42129 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:89657 On 04/30/2015 11:59 AM, Cecil Westerhof wrote: > I implemented happy_number function: > _happy_set = { '1' } > _unhappy_set = set() > > def happy_number(n): > """ > Check if a number is a happy number > https://en.wikipedia.org/wiki/Happy_number > """ > > def create_current(n): > current_array = sorted([value for value in str(n) if value != '0']) > return (current_array, ''.join(current_array)) > > global _happy_set > global _unhappy_set > > current_run = set() > current_array, \ > current_string = create_current(n) > if current_string in _happy_set: > return True > if current_string in _unhappy_set: > return False > while True: > current_run.add(current_string) > current_array, \ > current_string = create_current(sum([int(value) ** 2 > for value in current_string])) > if current_string in current_run | _unhappy_set: > _unhappy_set |= current_run > return False > if current_string in _happy_set: > _happy_set |= current_run > return True > > Besides it need some documentation: is it a good implementation? Or > are there things I should do differently? > > To decide for the values from 1 to 1E8 if it is happy or not, takes > 280 seconds. Not to bad I think. Also not very good. > First comment, if you're coding a complex implementation like this, take the time to do a brute force one as well. Then you can compare the results between brute force and your optimized one for all possible values, and make sure you haven't introduced any bugs. My brute force looks like: #Dave's version, brute force def davea1(n): cache = [] anum = str(n) newnum = 0 while newnum != 1: newnum = sum(int(i)*int(i) for i in anum) anum = str(newnum) if newnum in cache: return False #not a happy number cache.append(newnum) return True #found a happy number I then tried an optimized one, and my speed is only about 10% faster than yours for 1e7 loops. I show it anyway, since I think it reads a little better. And readability counts much more than a little performance. #optimizations: # cached happy and unhappy sets # sort the digits, and compare only the sorted values, without zeroes davea_happy = {1} davea_unhappy = set() SQUARES = dict((str(i), i*i) for i in xrange(10)) def davea2(n): global davea_happy, davea_unhappy cache = set() newnum = n while newnum != 1: newnum = int("".join(sorted(str(newnum)))) if newnum in davea_unhappy or newnum in cache: davea_unhappy |= cache return False #not a happy number if newnum in davea_happy: break cache.add(newnum) newnum = sum(SQUARES[ch] for ch in str(newnum)) davea_happy |= cache return True #found a happy number Finally, I did some testing on Jon Ribben's version. His was substantially faster for smaller sets, and about the same for 10*7. So it's likely it'll be slower than yours and mine for 10**8. But the real reason I didn't like it was it produced a much larger set of happy_numbers, which could clog memory a lot sooner. For 10**7 items, I had 3250 happy members, and 19630 unhappy. And Jon had 1418854 happy members. -- DaveA