(29C) Prime numbers up to 10'000 - Printable Version +- HP Forums (https://www.hpmuseum.org/forum) +-- Forum: HP Software Libraries (/forum-10.html) +--- Forum: General Software Library (/forum-13.html) +--- Thread: (29C) Prime numbers up to 10'000 (/thread-11319.html) |
(29C) Prime numbers up to 10'000 - Thomas Klemm - 09-02-2018 11:01 AM Inspired by Jürgen's video printing the Prime numbers up to 10'000 with an HP-19C I took a closer look at its sibling the HP-29C. It has 30 registers which is enough to store the primes up to 100. We can use indirect addressing with register 0 so I thought I give it a try. The first part (lines 01-35) calculates the primes up to 109 and stores them in the registers 3 to 29. We don't check numbers that are divisible by 2 and 3. Thus we start with 11, 13, 17, 19, 23, 25, … This is the sequence of numbers congruent to 1 or 5 mod 6: A007310. The difference of consecutive elements is: 2, 4, 2, 4, 2, 4, … We can calculate that sequence recursively (lines 10-13 and 39-42): \(\Delta n'=6-\Delta n\) For numbers smaller than 112 = 121 we only have to check divisibility by 5 and 7. In the second part (lines 36-60) we continue from 109 with 113, 115, 119, … and check only if they are divisible by primes smaller than their square root. Due to the lack of both the x<y and x≥y instruction we have to use the combination of two comparison instructions: Code: 49: 14 41 : x≤y ; n ≤ π(i)² ? The ISZ command has no upper limit. Thus we have to check that separately (lines 32-34). In the second part I've omitted that check. The program will stop with an error for the first prime bigger than 1092 = 11,881 which is 11,887. If you want a decent termination you can add a check after the PAUSE command. Here's what I came up with: Code: 01: 05 : 5 For those like me that don't have an HP-29C I recommend this online emulator. You can just copy the listing to the program that pops up when you click the display. This is the listing of the registers: Code: 00: i This list of the primes up to 11933 was copied from The First 10,000 Primes: Code: 2 3 5 7 11 13 17 19 23 29 The program should run as is on the HP-19C as well. You may want to replace the PAUSE statement with a PRx statement. Edit: Added comments and stack-diagrams to the code and hopefully some clarifications to the description. RE: (29C) Prime numbers up to 10'000 - PedroLeiva - 09-03-2018 01:14 PM (09-02-2018 11:01 AM)Thomas Klemm Wrote: Inspired by Jürgen's video printing the Prime numbers up to 10'000 with an HP-19C I took a closer look at its sibling the HP-29C. It has 30 registers which is enough to store the primes up to 100. We can use indirect addressing with register 0 so I thought I give it a try.Very nice. It also works for HP 67, physical device and emulators. Some simply instructions: To Start: RTN R/S To Stop: R/S To re-Start: R/S About Emulators, thanks for the links to Sydneysmith site, I did not know it, it must be recent, for HP 29 and HP 67 the links are: http://www.sydneysmith.com/products/hp29u/run/index.html http://www.sydneysmith.com/products/gss-hp67u/run/ Great, TYVM. Pedro RE: (29C) Prime numbers up to 10'000 - Dieter - 09-03-2018 05:24 PM (09-03-2018 01:14 PM)PedroLeiva Wrote: Very nice. It also works for HP 67, physical device and emulators. An HP67/97 version requires some changes, especially as the index register is no 0 but "I" which, unlike on the 19C/29C, is located at the end of the address range. This means that the test for the largest possible index has to be adjusted and you have to make sure that the last register "I" is not overwritten by a prime. Which is an important point as the original program is designed to terminate with an error message because of the attempt to access a register beyond 29. For the 67/97 storing something in R25 (="I") will not generate an error but it will mess up the program. On the other hand all indexes may be decreased by 1 as R0 is no longer required. Since I am not completely sure how the program works maybe Thomas can provide some more detailled hints for an HP67/97 version. I have a version running but I am not sure if it terminates correctly. (09-03-2018 01:14 PM)PedroLeiva Wrote: About Emulators, thanks for the links to Sydneysmith site, I did not know it, it must be recent, for HP 29 and HP 67 the links are: (...) I like the Panamatik emulators, both for the 19C (with printer) and the 67. Dieter RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-03-2018 05:46 PM I wondered how much better using A007310 and primes instead of odd numbers would be. The most expensive operation is probably checking if n is divisible by a probe π. Thus I counted how often that happens. n ∈ A007310 and π ∈ primes This is essentially the algorithm of my program from above: Code: primes = [ 5, 7, 11, 13, 17, 19, 23, 29, count = 28760 both n and π are odd numbers This is the algorithm used by Jürgen: Code: N = 10000 count = 55958 That is more by a factor of 1.9456. We can now gradually improve the algorithm and keep track of the changes. n ∈ A007310 and π is odd Code: N = 10000 count = 43625 n is odd and π ∈ {3} ∪ A007310 Code: A007310 = [ 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, count = 40350 both n and π are ∈ A007310 Code: A007310 = [ 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, count = 35354 This is better by a factor 1.5827 compared to the original program. And that's close to 3 ÷ 2 = 1.5 what I would have expected. However just using primes as probes is still better by a factor of 1.2292. That's probably since in the worst case of n being a prime all probes have to be checked. The length of primes is 27 and that of A007310 is 33. This gives a factor of 1.2222. Cheers Thomas RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-03-2018 07:05 PM (09-03-2018 05:24 PM)Dieter Wrote: Since I am not completely sure how the program works maybe Thomas can provide some more detailed hints for an HP67/97 version. If we have only 26 registers we can only store these 23 primes: [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] But since register I occupies register 25 instead of 0 the whole list is shifted. Thus the primes start with register 2 instead of 3 and so on. We can't reuse some of the numbers anymore when initialising the registers but that shouldn't be a problem. Code: 01: 2 You have to adjust the registers through out the whole program. In the for i loop we have to initialise register I with 2 now. And then we better switch this with calculating the next n: Code: 36: LBL 1 ; for n loop We have to make sure that the index is smaller than 25. Thus lines 32-33 have to be replaced by: Code: 30: ISZ ; i = i+1 The line numbering will probably be off as well. Since 97² = 9409 we can't go further than that. But the paper will run out anyway at about 9’300 so that's not a problem. If you want to make really sure that register I isn't overwritten you can add the same check at the end of the program: Code: 59: ISZ ; i = i+1 You may have noticed the Python programs in my last post. They might help understanding the algorithm. HTH Thomas PS: When I contains 25 the command RCL (i) will return 25. Thus it will be checked if 25 is a divisor of n. But we already checked if 5 is and that wasn't the case. Furthermore the previous probe was 97 which is bigger and thus n > 25². Thus we will happily increment I once more and get an error the next time we try to execute RCL (i). At least that's what I assume would happen. I haven't tested it yet. RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-03-2018 09:26 PM Listing for the HP-67: Code: 001: 02: 2 Registers: Code: 00: ∆n RE: (29C) Prime numbers up to 10'000 - Archilog - 09-04-2018 12:04 AM Hello, Great job! Jürgen Keller's video actually was inspired from a program published in Byte a long time ago. A guy called C.Ret did an awesome work which can be found there: Silicium ... In French. But the most interesting is easily readable. RE: (29C) Prime numbers up to 10'000 - Dieter - 09-04-2018 06:56 AM (09-03-2018 09:26 PM)Thomas Klemm Wrote: Listing for the HP-67: Shouldn't this be 2 STO I ? Otherwise my HP67-version is virtually identical. OK, I added LBL A at the beginning. ;-) Dieter RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-04-2018 09:31 AM (09-04-2018 06:56 AM)Dieter Wrote: Shouldn't this be 2 STO I ? Correct. I've updated the listing accordingly. Thanks a lot for the notification. Kind regards Thomas RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-04-2018 06:53 PM (09-04-2018 12:04 AM)Archilog Wrote: A guy called C.Ret did an awesome work which can be found there: Silicium Indeed it's amazing. Here's the flow-chart: And here's the program for the HP-19C: Quote:Pour imprimer les nombres premiers jusqu'à m (m compris entre de 50 et11449): This took me a while to figure out. Here's how to initialise the registers, say for m = 10,000. 4 STO 1 ; 4 instead of 5 10000 STO 2 7 STO 3 ; STO 3 instead of STO 1 200 STO 4 Otherwise register 1 is incremented in line 88 to 6 and then the heap starts there. But in line 29 it's hard-coded to use register 5. I'm currently running both programs side by side in the aforementioned emulator. His program is happily outrunning my program. Here's a listing for those who want to run it: Code: 01: 15 13 00 : LBL 0 ; Minimal initialization What's so cool about it? Instead of checking if a number is divisible by a prime the odd multiples of the primes are calculated starting from the square of the prime. For 7 that would be: 49, 63, 77, 91, 105, … Or for 11 that would be: 121, 143, 165, 187, … Of course we can't keep all of them since we have only 30 registers. But we don't have to. All we need is keeping the smallest among them. These numbers are kept in a min-heap and thus we only have to check the smallest of them which is in register 5. If the number is smaller it is a prime. If they are the same it's not a prime and we can proceed with the next number. If it is bigger we have to update the multiples of primes in the min-heap. And while doing so make sure it's still a min-heap. Thus some of the elements have to be rearranged. The prime is added as a decimal value to the multiples: For 7 that would be: 49.07, 63.07, 77.07, 91.07, 105.07, … This allows to calculate the next number. We can start with 7 since multiples of 2, 3 and 5 are omitted from the list of numbers. The main loop with the multiple GSB commands generates sequence A007775 (apart from 1 of course). I don't have a full understanding of the code for the min-heap yet. Thus if C.Ret is reading this post I'm eager to get some explanations from the master himself. Kind regards Thomas PS: Meanwhile we're at 1777 vs. 2137. The 2nd is of course C.Ret's program. Edit: Added comments and stack diagrams to the listing of the program. RE: (29C) Prime numbers up to 10'000 - pier4r - 09-04-2018 07:12 PM Amazing work, little question. How much time does it take on the 29C (real hw) ? RE: (29C) Prime numbers up to 10'000 - Albert Chan - 09-04-2018 07:31 PM (09-04-2018 06:53 PM)Thomas Klemm Wrote: A guy called C. Ret did an awesome work ... I had a similar (*) code in Lua to build list of primes. Instead of using min-heap, it uses Lua build-in hash table. https://github.com/achan001/PrimePi/blob/master/prime.lua (*) main difference is how sequence overlap is treated. Above sequence for 7 and 11 overlap at 231, 385, 539, 693, ... C. Ret code carried the prime along, ignoring the overlap. Lua code skip over the overlap, with the sequence carried the right step. Both approaches are equivalent. RE: (29C) Prime numbers up to 10'000 - C.Ret - 09-05-2018 08:12 PM (09-04-2018 06:53 PM)Thomas Klemm Wrote: This took me a while to figure out. Good catch! You are right Thomas! I have now to modify the explanations I gave in the Silicium forum. Yes, register 5 is "hard coded" to be the root of the min-heap, and I mistakenly indicate to initiate it to 5. I was developing several versions of this code, having great troubles to make it fit into the 99 steps of program memory. A previous version (not published) increase R1 after storing the new square and prime value into register – so I mess up starting value needed in register R1. Sorry for that and the wrong label for register R3 (I wrote 1 in the text when the correct register number is 3 as draw in the flow-chart). The first prime has to be put in register R3. You may have spent a few time to figure out the bug! Please accept my apologies. If we meet, please remind me to offer you a large glass of beer (or any local beverage you prefer). You also perfectly understand the philosophy of this algorithm. I try to reduce the number of tests needed to print the whole list of prims up to 10’000. Detecting prims by trying divisibility factors need a bunch of divisions. As you propose storing prime factor in memory is a great enhancement; the HP-19/29 has enough register to store all prime factors to achieve testing odd integer candidate up to 10’000. In the same time, filtering candidate to test may reduce the effort. That what you made, generating a sequence {+4 +2 +4 +2 +4 +2 … } drastically reduce the number of composite candidates not prime. Unfortunately, detecting prime by successive factor divisibility is most efficient for composite (multiple) numbers; as soon as a divisor is found, we jump to next candidate. For prime, we must test all the possible factors along (i.e. up to the square root); no shortcut to next candidate. Using a more sophisticated sequence, such as the one I have used ( i.e. {+4 +2 +4 +2 +4 +6 +2 +6 …} ) don’t help much; it reduce only by a few the “composite candidates” which are easy to detect and keep all the “long-testing true prime candidates” for divisibility tests. This observation lead me to try to find another way for detecting prims. A way which may be as simple as a test. If the HP-19/29 had have more than 10’000 registers a sieve of Eratosthene may have been a easy way. So I look around to see if other sieve or tricky algorithms may help. The sieve of Sundaram appears to be especially adapted as far as an adapted data structure can be bring out. As you correctly explain, we can't store in registers all the multiples tables produced by Sundaram’s algorithm. We only need to store the first value of all the sequences generated for each factor. We don’t need more register than for the divisibility testing method. But we need a convenient way to keep track of progress and to always know what the minimal value is. This can be a challenge and can ruin the effort. A data structure can be of great advantage here, since it allows keeping track of minimum value with an optimal number of tests. The min-Heap structure is maintained so that his root (register 5) always contains the minimal composite candidate generated by any of the already encounted primes. Please note that this data-structure was only invented a few years before the HP-19C was built. Implementing a min-Heap in the HP-19/29 in 1972 may have been consider as really top new technology (Tip top Hype ! What was the words at this time ?) By luck, the extra work to maintain the min-Heap will not ruin the effort because of two happy-facts: First, we don’t need a full implementation of the min-Heap; since the Sundaram algorithm makes that any new sequence entry (for a new discovered prime factor) start as far as the square of this factor. So we don’t need “pop up” procedure, any new sequence entry can be securely add at the very end of the heap (pointed by value in register R1). Second, the heap is short and not fully sorted. It is a binary structure, each node have two children’s which are greater (or egual). Less than 31 values mean that this binary tree is less than 4 levels high. When the tested candidate reach the min-heap root value, this value is increased by two times the factor. Then less than 4 values in the heap must be tested in order to replace (“push down”) the update root value. In fact, only the top of the min-heap is active, middle range is quite passive (each level divide by 2 activity due to binary structure) and the bottom is a long time in stand-by until square values are reaches. This made the code quite competitive despite the cost of extra test and the operations needed to maintain the heap in the case of a sophisticated filtered candidate. In contrary to the divisibility test methods, sieve methods didn’t lose the advantage of a few composite candidate number. If we test all integers or all odd integers in a row, clearly all the over-costs for min-Heap operations make this algorithm slow. With sophisticated filtered candidate, the min-Heap method gains advantage since primality is detected with only one test. The way the Sundaram sequences are built spare operating time of the min-Heap. But a lot of time is lost since the HP-19/29 have only one indirect register. Having two indirect registers may have simplify the operations for testing and swapping nodes in the binary heap and may have consequently greatly reduce running time. Another difficulty is that the heap didn’t start at R0: or R1:, the off-set make the computation of node's and children's indices a bit Strange: j = 2*i or j = 2*i+1 is transformed as: j = (i-2)*2 or j = (i-2)*2+1 As Albert Chan notice it, my code ignore overlapping value. This is true and done this way expressly; overlapping value (i.e. same value in the sequence of different factors) intersecting the sophisticated filtered candidate sequence are quite rare and the expenses of code and supplementary tests needed to detect them didn’t enter the program memory. Moreover, due to the fact than the candidate are unequally spaced (+2 +4 or +6), for each candidate, more than one pre-compute multiple may have to be update. That why after the “min-Heap maintenance loop” the program returns to label 5 where actual candidate is tested again versus the new min-Heap root (aka Register 5) which may be an overlapping value or not. This make no difference. In theory, avoiding overlapping value will reduce heap stack operations number. In practice, even with more elaborated code no significant gain was observed. RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 09-07-2018 08:07 PM (09-05-2018 08:12 PM)C.Ret Wrote: You may have spent a few time to figure out the bug! Finding the bug was easy once I was sure that I entered the code correctly into the emulator. But I've long been convinced that I made a typo somewhere. And then there's a weird behaviour of the emulator that wouldn't allow me to single-step into a subroutine (e.g. GSB 8). Instead it would just step over it and return. I assume that this is a bug of the emulator. But maybe someone can verify if that happens with a real calculator as well? This made me insert an R/S command directly after the LBL 8. Quote:Please accept my apologies. If we meet, please remind me to offer you a large glass of beer (or any local beverage you prefer). Are you planning to attend the Allschwil Meeting 2018? Because that would be an opportunity to meet. I'll happily accept a beer, although that's not really necessary. These kind of errors happen to me all the time and I'm glad I have Dieter who draws my attention to them, as in post #8 of this thread. I'm very impressed by your flowchart and listing of the program. What do you use to create them? It appears that the listing can be executed. Or how else should I interpret the smaller grey numbers like 1227x to the left of the line number? Thanks a lot for your explanations! Kind regards Thomas RE: (29C) Prime numbers up to 10'000 - rprosperi - 09-07-2018 09:16 PM (09-07-2018 08:07 PM)Thomas Klemm Wrote: I'm very impressed by your flowchart and listing of the program. Indeed the color-coded system of documenting the flowchart and synchronized source code is quite interesting, I've not seen anything like this before except in very expensive high-end professional software design tools, and that was a long time ago, so no idea if that caught-on. How were these made, I too am curious about the numbers embedded in the listings? Please explain this when time allows. Thanks for sharing this. RE: (29C) Prime numbers up to 10'000 - Archilog - 09-09-2018 02:22 AM (09-07-2018 08:07 PM)Thomas Klemm Wrote: I'm very impressed by your flowchart and listing of the program. Those numbers represent the number of times the instruction is executed (did I say it is an awesome work?). RE: (29C) Prime numbers up to 10'000 - Jurgen Keller - 11-11-2018 09:35 AM It looks like I missed a lot of interesting stuff during my involuntary timeout. I'm glad that my video inspired other people to going deeper into this topic. It's always a great pleasure to read Thomas' (in HP terms) "well-engineered" articles. That's the kind of brainfood I need to keep a clear and fresh mind :-) RE: (29C) Prime numbers up to 10'000 - Dieter - 11-11-2018 05:15 PM (09-07-2018 08:07 PM)Thomas Klemm Wrote: And then there's a weird behaviour of the emulator that wouldn't allow me to single-step into a subroutine (e.g. GSB 8). No, that's authentic HP behaviour. ;-) Indeed there are calculators where SST at a subroutine call executes that routine as a whole. This may have advantages sometimes, on other occasions it requires an immediate [R/S] to continue step by step. The HP67 and 97 behave the same way. It looks like this is one of the many cases where HP deliberately changed the behaviour of their calculators – consistency obviously what not the most important design goal. Dieter RE: (29C) Prime numbers up to 10'000 - Dieter - 11-11-2018 05:21 PM (11-11-2018 09:35 AM)Jurgen Keller Wrote: It looks like I missed a lot of interesting stuff during my involuntary timeout. Welcome back, Jürgen. ;-) Dieter RE: (29C) Prime numbers up to 10'000 - Thomas Klemm - 12-20-2018 06:08 AM Meanwhile C.Ret adapted the program for the HP-41C: Here's the same program for the HP-42S: Code: 00 { 188-Byte Prgm } And this is the print-out after running: 1E4 XEQ "VPRIMS" Cheers Thomas PS: Instead of ARCL here: Code: 90 ARCL ST Y We could use AIP: Code: 88 R↓ And thus get rid of these lines: Code: 03 FIX 00 |