An efficient method for computing Ulam numbers Introduction - viXra.org

5 downloads 0 Views 378KB Size Report
int pvi = index[j]; boolean more = true; while(more) { kk4++; int i = nx[pvi]; long ai = getUlam(i); double rdi = mod(ai,lamda)/lamda; if(i == 0) { more = false;. } ...
An efficient method for computing Ulam numbers Philip Gibbs The Ulam numbers form an increasing sequence beginning 1,2 such that each subsequent number can be uniquely represented as the sum of two smaller Ulam numbers. An algorithm is described and implemented in Java to compute the first billion Ulam numbers.

Introduction At first sight the Ulam numbers appear to be pseudo-random. If this were the case their asymptotic density would be expected to fade towards zero at high numbers as the number of possibilities for forming sums from previous numbers increases. In actuality the density decreases at first but settles around a distribution where about one in 13.5 numbers are in the sequence. A closer inspection shows that the numbers fall into dense clumps occurring about every 22 integers with sparser breaks between. Steinerberger performed a Fourier analysis on the sequence using the first 10 million numbers and found a clear signal with an angular frequency given by 𝛼 = 2.5714474995 [1]. This corresponds to a wavelength of 𝜆 =

2𝜋 𝛼

=

2.443443 … which is approximately 22/9 so the clumping apparently repeats about every nine wavelengths. When the frequency of Ulam numbers are plotted against their residue modulo 𝜆 they are found to have a non-uniform distribution concentrated in two peaks in the middle third of the wavelength. Figure 1 shows the distribution plotted from the first billion Ulam numbers counted in 1200 bins and normalised so that the vertical axis shows the probability for a large positive integer with a given residue to be Ulam. Ulam numbers whose residue falls outside this middle range are outliers. The outliers are relatively rare. There are only 1828 outliers in the first billion Ulam numbers and empirically the number of outliers less than a given Ulam number is less than its cube root for sufficiently large numbers. However they are important since almost all Ulam numbers are formed from a sum including one outlier and they control the shape of the distribution [2].

0.5 0.45 0.4 0.35 0.3 0.25 0.2 0.15 0.1

0.05 0

Figure 1: probability of being Ulam as a function of residue modulo 𝜆.

Computing the Ulam Numbers The most straight forward way to compute the Ulam numbers 𝑎𝑛 is to build up the sequence from the start testing each subsequent positive integer 𝑡 to see if it is the sum of two previous Ulam numbers. This can be done by simply taking 1

previous Ulam numbers 𝑎𝑛 < 2 𝑡 and checking to see if 𝑡 − 𝑎𝑛 is in the list of Ulam numbers so far constructed. The search for each number 𝑡 can be stopped once two sums have been found but if 𝑡 is an Ulam number the search will have to continue until all possible sums have been checked. If we assume that the density of Ulam numbers has a constant positive density then the computation time for the first 𝑛 numbers using this method is 𝑂(𝑛2 ). To compute the first billion Ulam numbers in a reasonable timeframe a more efficient method is required. An alternative for testing the number 𝑡 is to sort the smaller numbers 𝑎𝑛 according to their residue 𝑟𝑛 modulo 𝜆. If 𝑡 itself has a residue 𝑟 modulo 𝜆 and if 𝑎𝑛 + 𝑎𝑚 = 𝑡 then 𝑟𝑛 + 𝑟𝑚 = 𝑟 or 𝑟 + 𝜆. This means

1

that one of the residues 𝑟𝑛 or 𝑟𝑚 must be in one of the ranges 0 < 𝑟𝑘 < 2 𝑟 or 1 2

(𝑟 + 𝜆) < 𝑟𝑘 < 𝜆 Therefore it is only necessary to test smaller Ulam number

𝑎𝑘 whose residue 𝑟𝑘 lies in these ranges to see if it forms a sum. This search method works for any value of 𝜆 but if the 𝜆 we use is (close to) the recently discovered natural wavelength of the Ulam sequence then the number of Ulam numbers with residues in these ranges will be much less than half the number of previous Ulam numbers. If 𝑟 lies in the central third of the range as is the case for most Ulam numbers then we only need to test the subset of the outliers which lie in these ranges to determine if 𝑡 is Ulam. If 𝑟 lies outside the central third there will be a section from the denser portion of the distribution in the ranges, but in this case we usually find two sums very quickly and can rule out the possibility that 𝑡 is Ulam. In practice we have found that only about 5 tests are required on average to determine whether a number is Ulam using this method. The efficiency of the algorithm therefore depends on the ability to maintain a list of the previous Ulam numbers sorted by their residues that can be rapidly traversed from wither end. When each new Ulam number is found it must be inserted in the list. To find the correct place to insert it quickly we can use a binary search or maintain an index and once we have found the correct place to insert we need to form a linked list structure for rapid insertion avoiding the need to shift up all the subsequent entries in the list. This can be done by using built in data structures such as Treemaps in Java but for simplicity and transparency we have used custom structures based on ordinary arrays. The Java code used is shown in the Annex below. With this implementation the running time to compute the first billion Ulam numbers is less than one hour on an ordinary PC. The limiting factor which makes is hard to go to higher numbers is memory space rather than computation speed. With some space optimisations the program ran on a machine with 16 Gigabytes of RAM and this would need to be extended in proportion to the number to be calculated.

Results For the purposes of comparison we provide a table of example Ulam numbers 𝑛 100,000 1,000,000 10,000,000 100,000,000 158,311,381 200,000,000 317,670,407 500,000,000 1,000,000,000

𝑎𝑛 1,351,223 13,509,072 135,160,791 1,351,856,726 2,140,095,565 2,703,579,147 4,294,217,754 6,758,780,604 13,517,631,473

It should be mentioned that the Ulam number for 𝑛 = 100,000,000 agrees with an independently calculated value noted in the Online Encyclopaedia of Interger Sequences computed by Jud McCranie (sequence A002858). However the values for 𝑛 = 158,311,381 and 𝑛 = 317,670,407 are in disagreement. Therefore these numbers should not be relied on until a third independent implementation has verified the numbers. The value for the wavelength is computed to be 𝜆 = 2.44344296778474 with the corresponding frequency 𝛼 = 2.57144749847630. The largest gap in the first billion Ulam numbers was found to be 966291200 - 966290117 = 1083.

References [1] S Steinerberger, A Hidden signal in the Ulam sequence. arXiv:1507.00267 [math.CO] [2] P Gibbs, A conjecture for Ulam Sequences. viXra:1508.0045

Annex: Java Code public class Ulam { static static static static static

int int int int int

maxn = 1000000000; a[] = new int[maxn+1]; nx[] = new int[maxn+1]; pv[] = new int[maxn+1]; k[] = new int[maxn/2];

// // // //

list of ulam numbers next when ordered by residue previous when ordered by residue true for ulam numbers (packed bits)

static int nindex = maxn/100; static int index[] = new int[nindex]; static int nbin = 12000; static int bins[] = new int[nbin]; static static static static static

long long long long long

kk1=0; kk2=0; kk3=0; kk4=0; kk5=0;

static double lamda = 2.44344296778474; static double step = 13.517831473; public static void main(String[] args) { double alpha = 2.0*Math.PI/lamda;

// bin Ulam numbers by residue // should be mulyiple of 3 to separate outliers

double lamdarun = lamda;

System.out.println("lamda = "+lamda); System.out.println("alpha = "+alpha); initUlam(); // initialise index for(int i=0; i 0.80) {

// to mind the gap use the brute search

int j = n; // better to start from larger end long aj = getUlam(j); while(more && 2*aj > a0) { kount2++; long a1 = aj; long a2 = a0-a1; kk3++; if(isUlam(a2)) { if(ulam) { // found more than one sum ulam = false; more = false;

} else { ulam = true; } } j--; aj = getUlam(j); } more = false; }

long a1x = 0; int kount0 = 0; int i = nx[0]; // start with smallest residue long ai = getUlam(i); double rdi = mod(ai,lamda)/lamda; while(2*rdi 2.0/3.0 || z < 1.0/3.0) { lamdarun = (lamdarun*9.0+p)/10.0; System.out.println(nor+" "+nol+" "+n+" "+a0+" "+z+" "+p+" "+lamdarun); System.err.println(n+" "+a0+" kk: "+(kk1/a0)+" "+(kk2/a0)+" "+ (kk3/a0)+" "+(kk4/a0)+" "+(kk5/a0)); }

if(n==1000 || n==10000 || n==100000 || n==1000000 || n==10000000 || n==50000000 || n%100000000 == 0 || n==158311381 || n==317670407) { System.out.println("a["+n+"] = "+a0); } long a1 = getUlam(n-1); long gap = a0-a1; if(gap > bestgap) { bestgap = gap; System.out.println(n+" "+a0+" - "+a1+" = "+gap+" is bigger gap"); } // build distribution by residue in bins int ibin = (int)(z*nbin); bins[ibin]++; } } System.out.println("a["+n+"] = "+getUlam(n)); System.out.println("biggest gap was "+bestgap); double density = ((double) n)/((double) getUlam(n)); System.out.println("density = "+density); System.out.println("step = "+(1.0/density));

System.out.println(""); System.out.println("bin frequencies:"); for(int ibin=0; ibin rdn) System.err.println("links are not ordered at "+ pvi+","+nx[pvi]+" "+rdi+" > "+rdn); m++; pvi = nx[pvi]; }

if(m != n) System.err.println("link list is wrong length "+m+" != "+n); System.err.println("links check complete"); } // booleans flagging the ulam numbers are packed to save space static int pow2[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768, 1

Suggest Documents