0

Hi guys I'm looking for a program to find nth number of Golomb sequence without using array!!!!

** I know this below program, But it's so so slow...

#include <bits/stdc++.h>
using namespace std;
int findGolomb(int);
int main()
{
  int n;
  cin >> n;
  cout << findGolomb(n);
  return 0;
}
int findGolomb(int n)
{
  if (n == 1)
    return 1;
  else
    return 1 + findGolomb(n - findGolomb(findGolomb(n - 1)));
}
Knddk
  • 1
  • 1) Why don't you want to use arrays? 2) This is (probably) the best solution if you don't want to use an array. You have `O(1)` space but the time complexity is definitely bigger. – justANewb stands with Ukraine Dec 16 '21 at 13:08
  • 2
    Another thing. [`#include `](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) and [`using namespace std;`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) are both bad practice. – justANewb stands with Ukraine Dec 16 '21 at 13:13
  • any implementation is a tradeoff, often between required memory and computation time. If you dont want to store anything, you have to recompute each result. There (probably) is no solution that takes no memory but also is as fast as the version requiring to store previous results... – codeling Dec 16 '21 at 13:15
  • Hi justANewbie. Thanks for your tips and information. I just wanna know the another way to solve this problem, and more practice for recursive functions... and I believe it has another solve, but I couldn't find it! – Knddk Dec 16 '21 at 13:24
  • I updated my comment to include the details of the "best" solution that is about as fast as using an array of size `n`, yet it stores many fewer than `n` values. You only need about 30,000 stored values to calculate `g(n)` up to about `n == 2000000`. You can extend this logic to handle any value of `n` that doesn't overflow. You might be able to use very few values by figuring out what the values of the generated arguments into `g` must be. For example, in `g(n - 1)`, you know you always need to store `n - 1` to calculate `g(n)`. – user904963 Dec 24 '21 at 21:31

2 Answers2

0

It depends on how large a value you want to calculate. For n <= 50000, the following works:

#include <cmath>
/*
 */
 round(1.201*pow(n, 0.618));

As it turns out, due to the nature of this sequence, you need almost every single entry in it to compute g[n]. I coded up a solution that uses a map to save past calculations, purging it of unneeded values. For n == 500000, the map still had roughly 496000 entries, and since a map has two values where the array would have one, you end up using about twice as much memory.

#include <iostream>
#include <map>

using namespace std;

class Golomb_Generator {
public:
    int next() {
        if (n == 1)
            return cache[n++] = 1;
        
        int firstTerm = n - 1;
        int secondTerm = cache[firstTerm];
        int thirdTerm = n - cache[secondTerm];
        
        if (n != 3) {
            auto itr = cache.upper_bound(secondTerm - 1);
            cache.erase(begin(cache), itr);
        }

        return cache[n++] = 1 + cache[thirdTerm];
    }
    
    void printCacheSize() {
        cout << cache.size() << endl;
    }
    
private:
    int n = 1;
    map<int, int> cache;
};
 
void printGolomb(long long n)
{
    Golomb_Generator g{};
    
    for (int i = 0; i < n - 1; ++i)
        g.next();
        
    cout << g.next() << endl;
    g.printCacheSize();
}
 
int main()
{
    int n = 500000;
    printGolomb(n);
    return EXIT_SUCCESS;
}

You can guess as much. n - g(g(n - 1)) uses g(n-1) as an an argument to g, which is always much, much smaller than n. At the same time, the recurrence also uses n - 1 as an argument, which is close to n. You can't delete that many entries.

About the best you can do without O(n) memory is recursion combined with the approximation that is accurate for smaller n, but it will still become slow quickly. Additionally, as the recursive calls stack up, you will likely use more memory than having an appropriately sized array would.

You might be able to do a little better though. The sequence grows very slowly. Applying that fact to g(n - g(g(n - 1))), you can convince yourself that this relationship mostly needs stored values nearer to 1 and stored values nearer to n -- nearN(n - near1(nearN(n - 1))). You can have a tremendous swath in between that do not need to be stored, because they would be used in calculations of g(n) for much, much larger n than you care about. Below is an example of maintaining the first 10000 values of g and the last 20000 values of g. It works at least for n <= 2000000, and it stops working for sure at n >= 2500000. For n == 2000000, it takes about 5 to 10 seconds to compute.

#include <iostream>
#include <unordered_map>
#include <cmath>
#include <map>
#include <vector>

using namespace std;

class Golomb_Generator {
public:
    int next() {
        return g(n++);
    }
    
private:
    int n = 1;
    map<int, int> higherValues{};
    vector<int> lowerValues{1, 1};
    
    int g(int n) {
        if(n == 1)
            return 1;
        
        if (n <= 10000) {
           lowerValues.push_back(1 + lowerValues[n - lowerValues[lowerValues[n - 1]]]);
           return higherValues[n] = lowerValues[n];
        }
        
        removeOldestResults();
            
        return higherValues[n] = 1 + higherValues[n - lowerValues[higherValues[n - 1]]];
    }
    
    void removeOldestResults() {
        while(higherValues.size() >= 20000)
            higherValues.erase(higherValues.begin());
    }
};
 
void printGolomb(int n)
{
    Golomb_Generator g{};
    
    for (int i = 0; i < n - 1; ++i)
        g.next();
        
    cout << g.next() << endl;
}
 
int main()
{
    int n = 2000000;
    printGolomb(n);
    return EXIT_SUCCESS;
}
user904963
  • 1,520
  • 2
  • 15
  • 33
-1

There are some choices and considerations regarding the runtime.

  1. move the complexity to math

The algorithm actually is nothing else but math in computer language. The algorithm may be improved by mathematical substitutions. You may look into the research regarding this algorithm and may find a better algorithm substitute.

  1. move the complexity to the compiler.

When calling the findGolomb(12) with a specific number known at compile time, we may use constexpr, to move the calculation time to the compiler.

constexpr int findGolomb(int);
  1. move the complexity to the memory

Although requested by the Question to not use an array, this is a considerable constraint. Without using any additional memory space, the algorithm has no options but to use runtime, to for example, to known already computed values of findGolomb(..).

The memory constraint may also include the size of the compiled program (by additional lines of code).

  1. move the complexity to the runtime

When not using math, compiler or memory to enhance the algorithm, there is left no options but to move the complexity to the runtime.

Summarizing, there won't be any options to improve the runtime without the four options above. Removing compiler and memory optimizations, and considering the current runtime as already optimal, you are only left with math and research.