aboutsummaryrefslogtreecommitdiff
path: root/challenge-054/user-person/python/ch-2.py
blob: 46c8613e792f76a0ac70c21941fcaef1808aa183 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3

###########################################################################
# script name: ch-2.py                                                    #
#                                                                         #
# https://github.com/user-person                                          #
#                                                                         #
# https://perlweeklychallenge.org/blog/perl-weekly-challenge-054/         #
#                                                                         #
# Collatz Conjecture                                                      #
# Contributed by Ryan Thompson                                            #
#                                                                         #
# It is thought that the following sequence will always reach 1:          #
# $n = $n / 2 when $n is even                                             #
# $n = 3*$n + 1 when $n is odd                                            #
#                                                                         #
# For example, if we start at 23, we get the following sequence:          #
# 23 -> 70 -> 35 -> 106 -> 53 -> 160 -> 80 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1   #
#                                                                         #
# Write a function that finds the Collatz sequence for any positive       #
# integer.                                                                #
# Notice how the sequence itself may go far above the original starting   #
# number.                                                                 #
#                                                                         #
# Extra Credit                                                            #
# Have your script calculate the sequence length for all starting numbers #
# up to 1000000 (1e6), and output the starting number                     #
# and sequence length for the longest 20 sequences.perl                   #
#                                                                         #
###########################################################################

import os
import re
import sys

def msgExit():
    print(os.path.basename(sys.argv[0]),  "requires one positive integer as an argument (or extracredit).")
    exit(1)

high = {}

def checkHighKeys(key,keyCount,highKeys):
    if len(highKeys) == 20:
        highKeys = {k: v for k, v in sorted(highKeys.items(), key=lambda item: (item[1],item[0]))}
        if highKeys[list(highKeys)[0]] < keyCount:
            del highKeys[list(highKeys)[0]]
            highKeys[key] = keyCount
        elif highKeys[list(highKeys)[0]] == keyCount:
            if list(highKeys)[0] < key:
                del highKeys[list(highKeys)[0]]
                highKeys[key] = keyCount
    else:
        highKeys[key] = keyCount

    return highKeys

if len(sys.argv) != 2:
    msgExit()

n = sys.argv[1]
extraCredit = False
ecPattern = re.compile(r'-{0,2}extra[-~`!@#$%^&*=+|\\;:",.?/ ]?credit', flags=re.IGNORECASE)

if  ecPattern.search(str(n)):
    n = 2
    extraCredit = True
    sys.stderr.flush()
    sys.stderr.write('Allow time for calculations.\n')
    sys.stderr.write("        They finish when '#'s reach this point-> |\n")

elif re.search(r'\A\d+\Z',str(n)):
    n = int(n)
    if n < 1:
        msgExit()
    if n == 1:
        print(1)
        exit()
else:
    msgExit()
    
MAX = 1000000
i = n

while i <= MAX:
    count = 0

    while n != 1:
        if extraCredit:
            count += 1
        else:
            print(int(n),'-> ', end='',flush=True)

        if n % 2 == 0:          # EVEN
            n /= 2
        else:                   # ODD
            n = 3*n + 1

    if extraCredit:
        high = checkHighKeys(i, count, high)
        i += 1
        n = i

        if n % 20000 == 0:
            sys.stderr.write('#')
            sys.stderr.flush()
    else:
        print(1)
        break

if extraCredit:
    sys.stderr.write('\n\n')
    high = {k: v for k, v in sorted(high.items(), key=lambda item: (item[1],item[0]),reverse=True)}
    for thisKey in high.keys():
        print('Starting number:', thisKey, 'with sequence length:',  high[thisKey])