![]() |
| The mathematician who first thought of this idea: Emile Lemoine |
Here are three examples of "lemoine partitions", i.e. where the odd numbers are broken down into their constituent elements based on the definition above.
$$7 = 3 + 2 * 2$$
$$9 = 3 + 2 * 3$$
$$11 = 5 + 2 * 3$$
In 1999, Dann Corbit was able to verify the conjecture to 1,000,000,000 numbers utilizing the following code in C (https://groups.google.com/forum/?hl=en#!msg/sci.math/HoCpH8gPDHM/gtU-fuSWnFMJ):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define TEST( f, x ) ( *( f+( x )/16 )&( 1<<( ( ( x )%16L )/2 ) ) ) #define SET( f, x ) *( f+( x )/16 )|=1<<( ( ( x )%16L )/2 ) unsigned ip(unsigned long j, unsigned char *d) { if (j == 1) return 0; if ((j % 2) == 0) { if (j == 2) return 1; else return 0; } else return (!(*(d + (j) / 16) & (1 << (((j) % 16L) / 2))) && j != 1); } int main(int argc, char *argv[]) { unsigned char *feld = NULL, *zzz = NULL; unsigned long teste = 1, max, mom, hits = 1, alloc; time_t begin; unsigned char quiet = 0; if (argc > 1) { max = atol(argv[1]) + 10000; if (argc > 2) quiet = 1; } else max = 1000000000L; zzz = feld = malloc(alloc = (((max -= 10000L) >> 4) + 1L)); if (feld) { char found; memset(zzz, 0, alloc); printf("Searching prime numbers to : %lu\n", max); begin = time(NULL); while ((teste += 2) < max) if (!TEST(feld, teste)) { ++hits; for (mom = 3L * teste; mom < max; mom += teste << 1) SET(feld, mom); } printf(" %ld prime numbers found in %.2f secs.\n\n", hits, difftime(time(NULL), begin)); { long o, p, q, j; for (o = 7; o <= max; o += 2) { found = 0; for (j = 2; j < o;) { p = j; q = o - 2 * p; if (ip(p, feld) && ip(q, feld)) { if (!quiet) printf("%lu = 2*%lu + %lu\n", o, p, q); found = 1; break; } if (j == 2) j++; else j += 2; } if (!found) printf("*** Problem!! %lu\n", o); } free(feld); printf(" Finished calculations in %.2f secs.\n\n", difftime(time(NULL), begin)); } } else { puts("Memory allocation failure."); exit(EXIT_FAILURE); } return 0; }
A new verifier was implemented in Python to try and either match/exceed this number.
""" Description: Traditional Implementation of Lemoine's Conjecture """ import numpy as np import time def primesfrom2to(n): # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 """ Input n>=6, Returns a array of primes, 2 <= p < n """ sieve = np.ones(n/3 + (n%6==2), dtype=np.bool) sieve[0] = False for i in xrange(int(n**0.5)/3+1): if sieve[i]: k=3*i+1|1 sieve[ ((k*k)/3) ::2*k] = False sieve[(k*k+4*k-2*k*(i&1))/3::2*k] = False return np.r_[2,3,((3*np.nonzero(sieve)[0]+1)|1)] def main(): start_time = time.time() n = 7 x = 0 l = list(primesfrom2to(1000000000)) sl = set(l) print("Done") print("--- %s seconds ---" % (time.time() - start_time)) while (n < 1000000000): if(n-(2*l[x]) in sl): n = n+2 x = 0 x = x + 1 print("Done 2") print("--- %s seconds ---" % (time.time() - start_time)) main()
This version relied on a pre-generated list of prime numbers in both list and set form to generate odd numbers sequentially. It rearranges the formal definition of Odd # = 2q + p to Odd # - 2q = p to take advantage of python's native set data type and enhanced searching capabilities. Replacing these with just lists, numpy arrays, or iterators was considered but not successfully implemented in a time-conserving manner.
You can track the progress of the generator by inserting this code snippet within the if statement within the while loop within the main method.
if n % 1000 == 1: print("\r{}/{}".format(n, 100000))
Here is a table with the speed results from this implementation. The program reached 2*10^9 before stressing the hardware due to lack of available memory.
Odd #’s up to ___ checked
|
Time
|
10,000
|
--- 0.0129 seconds
---
|
100,000
|
--- 0.129 seconds
---
|
1,000,000
|
--- 1 seconds ---
|
10,000,000
|
--- 11 seconds ---
|
100,000,000
|
--- 138 seconds ---
|
1,000,000,000
|
--- 1593 seconds
---
|
2,000,000,000
|
--- 3,487 seconds
---
|
Another approach would be to generate the results from all of the combinations of p + 2q and match those up to a list of the odd numbers greater than or equal to seven. This was implemented with the following program.
""" Description: New strategy to tackle verifying Lemoine's conjecture """ import numpy as np import time from operator import add def primesfrom2to(n): # https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 """ Input n>=6, Returns a array of primes, 2 <= p < n """ sieve = np.ones(n/3 + (n%6==2), dtype=np.bool) sieve[0] = False for i in xrange(int(n**0.5)/3+1): if sieve[i]: k=3*i+1|1 sieve[ ((k*k)/3) ::2*k] = False sieve[(k*k+4*k-2*k*(i&1))/3::2*k] = False return np.r_[2,3,((3*np.nonzero(sieve)[0]+1)|1)] def cartesian_add(arr1, arr2): arr1_e = np.repeat(np.expand_dims(arr1, 1), arr2.size, axis=1) arr2_e = np.repeat(np.expand_dims(arr2, 0), arr1.size, axis=0) return arr1_e + arr2_e def main(): start_time = time.time() list1 = primesfrom2to(1000000) list2 = 2 * list1 list4 = np.arange(7,3000000,2) list4 = np.setdiff1d(list4,np.unique(cartesian_add(list1, list2))) print(list4) print("--- %s seconds ---" % (time.time() - start_time)) main()
While the original versions of this implementation utilized nested for loops (later changed to for loop containing a map(add,primes,semiprimes) function with rolling numpy primes loop), Ernie Parke contributed the cartesian_add method which shaved off several seconds (it runs about 4x faster without the use of the np.unique function than the original implementation).
The cartesian_add function essentially generates all of the combinations of addition while the set.diff1d function figures out which numbers are only a part of the set of odd numbers (originally represented as list4) and therefore haven't been verified.
![]() |
| Schematic of the principle utilized in the "new strategy." Source |
Jacob G. of Futuresight Technologies also developed a C++ code to to verify Lemoine's conjecture for a certain number of digits. Please note that it requires -fopenmp flag if you want to utilize the parallelism within the program to decrease run-time.
#include <iostream> #include <vector> #include <set> typedef unsigned long long ull; using namespace std; const ull maxNumber = 1000000000; bool numberWorks(ull n, vector<ull>& primes, set<ull>& primeSet) { ull factor; ull remainder; ull primeSize = primes.size(); ull semiPrime; for (ull i = 0; i < primeSize; i++) { factor = primes[i]; if (factor > n) { return false; } remainder = n - factor; if (remainder % 2 == 0 && primeSet.find(remainder / 2) != primeSet.end()) { return true; } } return false; } int main() { bool* primes = new bool[maxNumber]; bool* semiPrimes = new bool[maxNumber]; vector<ull> primeList; set<ull> primeSet; for (int i = 0; i < maxNumber; i++) { primes[i] = true; } for (int i = 2; i < maxNumber; i++) { if (primes[i]) { for (int j = i; j < maxNumber; j += i) { primes[j] = false; } primeList.push_back(i); primeSet.insert(i); } } cout << "Computed primes!" << endl; #pragma omp parallel for num_threads(8) for (ull numberToCheck = 7; numberToCheck < maxNumber; numberToCheck += 2) { if (!numberWorks(numberToCheck, primeList, primeSet)) { cout << "Number failed: " << numberToCheck << endl; } else if ((numberToCheck - 1) % 1000000 == 0) { cout << (numberToCheck / (double)maxNumber) * 100 << "%" << endl; } } delete[] primes; delete[] semiPrimes; }
He contributed the following speed readings:
Odd #’s up to ___ checked
|
Time
|
1,000,000
|
--- .6 seconds ---
|
10,000,000
|
--- 15 seconds ---
|
100,000,000
|
--- 301 seconds ---
|
Out of curiosity, the original C code was run again to see how efficient it was. Due to C being a compiled language, this test ended up being more successful than the python programs at and allowed the conjecture to be verified to 10^10 in a reasonable time period. Here are some times for those runs:
Odd #’s up to ___ checked
|
Time
|
1,000,000,000
|
--- 108 seconds ---
|
10,000,000,000
|
--- 1009 seconds
---
|
Therefore, it is concluded that the conjecture is now verified up to 10^10 through the original C code, and up to 2*10^9 through the new Python implementation.
Thank you to Ernie Parke and Jacob G. answering all of my questions throughout this process.
Sources:
https://en.wikipedia.org/wiki/Lemoine%27s_conjecture
http://mathworld.wolfram.com/LevysConjecture.html
https://planetmath.org/levysconjecture
https://primes.utm.edu/curios/page.php?number_id=82&submitter=Capelle
https://www.mathsteacher.com.au/year7/ch11_ratios/04_prob/unit.htm
https://oeis.org/A046927


great
ReplyDeleteThe article demonstrates how Python can be used to verify Lemoine’s conjecture through efficient prime-number generation, set-based searching, and different computational strategies. The use of NumPy arrays for generating primes, performing vectorized operations, and comparing execution times makes NumPy Online Course particularly relevant to the numerical-computing techniques discussed.
ReplyDeleteThe comparison between traditional iteration, Python sets, NumPy arrays, and Cartesian combinations also highlights the importance of choosing efficient data structures and algorithms when working with large computational problems. Learning these Python programming techniques through a Python Online Course can help build a stronger foundation for implementing and optimizing similar mathematical computations.
ReplyDelete