Showing posts with label input. Show all posts
Showing posts with label input. Show all posts

Sunday, 20 November 2022

Jupyter Notebook

I finally got around to installing SageMath on my laptop that is running Linux Mint. The installation was quite easy via this LInux Community website. I regularly use SageMathCell which is a free online instance of SageMath. It's quick and responsive but it will timeout if the calculations take too long.

Such was the case when I set out to calculate the weird numbers up to 40,000 using SageMathCell. The algorithm that I had written timed out. Figure 1 shows the algorithm that I wrote:

Figure 1

In reference to the algorithm, it's known that only abundant numbers can be weird and so I rule them out from the start. After that I simply look at all the proper subsets of the set of divisors to see if any of them sum to the number. If one of them does, then the number is pseudoperfect and we move on. It's the time taken to sum of all the elements in the subsets that is causing the algorithm to time out.

I hoped that by setting up the Jupyter notebook I wouldn't run into this timing out problem. The algorithm would use my computer's resources for as long as it took. As of writing this post, the algorithm has been chugging away for over six and a half hours. My laptop has four cores and one of them is always at 100%, though not always the same one. Figure 2 shows the situation.


Figure 2

Figure 3 shows the algorithm in the Jupyter worksheet. The black dot to the right of SageMath 9.0 at the top of the page indicates that the program is running. I find it hard to believe that the calculations haven't yet completed but I'll leave it running in the hope that it does eventually terminate.


Figure 3

There are of course ways to make this algorithm more efficient. As this source states:

All the known weird numbers are even ... a weird number multiplied by a prime larger than its sum of divisors is again weird, so there are infinitely many weird numbers. Primitive weird numbers are those which do not have a weird proper divisor. The initial ones are:

70, 836, 4030, 5830, 7192, 7912, 9272, 10792, 17272, 45356, 73616, 83312, 91388, 113072, 243892, 254012, 338572, 343876, 388076, 519712, 539744, 555616, 682592, 786208, 1188256, 1229152, 1713592, 1901728, 2081824, 2189024, 3963968, 4128448, ...

As I read this, I realised that there was no need to include odd numbers and so the processing time could be halved simply by excluding them. Furthermore, all weird numbers must have at least three distinct prime factors and so this condition could be included as well to speed up calculations. I've adjusted the algorithm in the Jupyter notebook  accordingly and restarted the calculation. See Figure 4. 


Figure 4

Actually, looking at the number of subsets involved, it's not surprising that the calculations are taking forever. Here are some examples of the numbers of subsets involved for certain numbers involved:

120 --> 32768

180 --> 131072

240 --> 524288

360 --> 8388608

720 --> 536870912

840 --> 2147483648

I had no idea that the numbers were of that magnitude.

Of course, the advantage of the notebook is that it accommodates input of data which is not possible in SageMathCell. See Figure 5.


Figure 5

Now that I have SageMath installed on my laptop I'm sure that I'll make more use of it.

Tuesday, 26 June 2018

Happy Numbers

Today I turned 25286 days old and 25286 happens to be a happy number, defined as:
Let us define a function \( s(n) \), for \( n>0 \), which gives the sum of the squares of the digits of \( n \), so, for example, \( s(37)=3^2+7^2=58 \).

If we start from a number \(n\)  and we repeatedly apply \( s(\cdot) \), we obtain a sequence \(S_n\) of numbers \(n\), \(s(n) \), \(s(s(n)\), \(\dots \), and so on.

A number \(n\) is called happy if \(S_n \) contains the number 1.

Note that \(s(1)=1 \), so in that case the sequence \( S_n \) has an infinite tail of \(1\)'s.

If a number is not happy then it is easy to see that at a certain point \(S_n\) will enter the infinite loop$$ \dots,4, 16, 37, 58, 89, 145, 42, 20, 4,\dots $$So, for example, starting from  \(94\)  we obtain \(94\rightarrow97\rightarrow130\rightarrow10\rightarrow1\), so \(94\) is happy. 

On the contrary, starting from 61 we obtain \(61\rightarrow37\rightarrow58\rightarrow89 \) and thus 61 is not happy, since 89 belongs to the unhappy loop.

According to Wikipedia: 
by inspection of the first million or so happy numbers, it appears they have a natural density of around 0.15. Perhaps surprisingly, then, the happy numbers do not have an asymptotic density. The upper density of the happy numbers is greater than 0.18577, and the lower density is less than 0.1138. After 25286, the next happy number is 25294 (followed by 25295).
As usual I tried to write some SAGE code to determine whether a number was happy or not. Here is what I came up with:
INPUT (using a happy number 25286):
entered_number=25286
number=str(entered_number)
while sum!= 1 and sum!=4:
    sum=0
    for x in range(len(number)):
        sum+=Integer(number[x])^2
    number=str(sum)
    print(sum) 
OUTPUT
133
19
82
68
100
1
INPUT (using an unhappy number 89)
entered_number=89
number=str(entered_number)
while sum!= 1 and sum!=4:
    sum=0
    for x in range(len(number)):
        sum+=Integer(number[x])^2
    number=str(sum)
    print(sum) 
OUTPUT
145
42
20
4
The 4 is used to prevent the program going into an infinite loop and, at the same time, to mark a number as happy. Unhappy numbers will always enter the following loop:

... 4, 16, 37, 58, 89, 145, 42, 20, 4, ...

Any of the above numbers could be used to identify an unhappy number and break the loop.

The Wikipedia article on unhappy numbers includes the following Python code for determining whether a number is happy or unhappy (rather than displaying the trajectory as I did in my SAGE program):
INPUT (using happy number 25286)
def square(x):
    return int(x) * int(x)
def happy(number):
    return sum(map(square, list(str(number))))
def is_happy(number):
    seen_numbers = set()
    while number > 1 and (number not in seen_numbers):
        seen_numbers.add(number)
        number = happy(number)
    return number == 1
is_happy(25286) 
OUTPUT
True
INPUT (using unhappy number 89)
def square(x):
    return int(x) * int(x)
def happy(number):
    return sum(map(square, list(str(number))))
def is_happy(number):
    seen_numbers = set()
    while number > 1 and (number not in seen_numbers):
        seen_numbers.add(number)
        number = happy(number)
    return number == 1
is_happy(89) 
OUTPUT
False 
As can be seen, the Python code involves a quite different approach. It defines three functions, the second building on the first and the third building on the second : square(x), happy(number) and is_happy(number).

The command sum(map(square, list(str(number)))) caught my eye. This is a very useful command that takes two inputs:
  • a function (in this case, square)
  • a list (in this case, str(number))
It outputs a list (in this case, the sum of the number's digits squared).