如何找到计算时间小于O(n)的以下类型的集合?

时间:2015-10-04 19:34:34

标签: algorithm math data-structures set time-complexity

Consider the following set to understand the problem.

这里显示了5个不同的集合。 S1包含1.下一组S2从S1计算,考虑以下逻辑:

  1. 假设Sn包含{a1,a2,a3,a4 .....,an},并且Sn的中间元素是b。
  2. 然后,集合Sn + 1包含元素{b,b + a1,b + a2,......,b + an}。总(n + 1)个元素。如果一个集合包含偶数个元素,那么中间元素是(n / 2 +1)。
  3. 现在,如果给出n作为输入,那么我们必须显示集合Sn的所有元素。 显然,有可能在O(n)时间内解决问题。

    我们可以将所有中间元素计算为(2 ^(n-1) - 前一组的中间元素+ 1),其中s1 = {1}是基本情况。以这种方式O(n)时间我们将得到所有中间元素直到第(n-1)组。因此,第(n-1)组的中间元素是第n组集合的第一个元素。 (第(n-1)组的中间元素+第(n-2)组的中间元素)是第n组的中间第二元素。通过这种方式,我们将获得第n组的所有元素。 所以它需要O(n)时间。

    这里是我编写的完整java代码:

    public class SpecialSubset {
    private static Scanner inp;
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    
        int N,fst,mid,con=0;
    
    
        inp = new Scanner(System.in);
        N=inp.nextInt();
        int[] setarr=new int[N];
        int[] midarr=new int[N];
        fst=1;
        mid=1;
        midarr[0]=1;
        for(int i=1;i<N;i++)
        {
            midarr[i]=(int) (Math.pow(2, i)-midarr[i-1]+1);
        }
        setarr[0]=midarr[N-2];
        System.out.print(setarr[0]);
        System.out.print(" ");
        for(int i=1,j=N-3;i<N-1;i++,j--)
        {
            setarr[i]=setarr[i-1]+midarr[j];
            System.out.print(setarr[i]);
            System.out.print(" ");
    
    
        }
        setarr[N-1]=setarr[N-2]+1;
        System.out.print(setarr[N-1]);
    
    
    
    }
    

    }

    以下是问题的链接:

    https://www.hackerrank.com/contests/projecteuler/challenges/euler103

    是否可以用少于O(n)的时间来解决问题?

2 个答案:

答案 0 :(得分:3)

@Paul Boddington给出了一个答案,依赖于这些集合中第一批数字的序列是Narayana-Zidek-Capell数字,并检查了它的一些小值。但是,没有证据证明这个猜想。这个答案是上述的补充,以使其完整。我不是HTML / CSS / Markdown大师,所以你不得不原谅下标的错误定位(如果有人能改进那些 - 请做我的客人。

表示法:

i j 成为第j组中的第i个数字。
我还将b j 定义为j-2 - 集的第一个数字。这是证明的序列。 -2是Narayana-Zidek-Capell序列中的第一个和第二个1

生成规则:

问题陈述没有说明“中心数”对于偶数长度集(真正的列表,但无论如何)是什么,但在这种情况下它们似乎意味着“正确的中心”。当我在下面使用它时,我将以粗体表示规则编号。

  1. a 1 1 = 1
  2. a 1 n = a ceil( n + 1 / 2 n-1
  3. a i n = a 1 n + a i-1 n-1
  4. b n = a 1 n-2
  5. 证明:

    第一步是通过稍微展开递归并替换b来为 i n 制定一个稍微复杂的公式:

    1. a i n =Σa 1 nj =Σb n-j + 2 } j
    2. 中的[0 ... i-1]

      接下来,我们考虑b n 的两种情况 - 一种n为奇数,一种n为偶数。

      即便如此:
      b 2n + 2 = a 1 2n =
        2 = a ceil( 2n + 1 / 2 2n-1 = a n + 1 2n-1 =
        3 = a 1 2n-1 + a n 2n-2 = <登记/>   2,4 = b 2n + 1 + a 1 2n-1 =
        5 = 2 * b 2n + 1

      奇怪的案例:
      b 2n + 1 = a 1 2n-1 =
        2 = a ceil( 2n / 2 2n-2 = a < sup> n 2n-2 =
        3 = a 1 2n-2 + a n-1 2n-3 =
        4 = 2 * b 2n +(a n-1 2n-3 - a 1 < / sup> 2n-2 )=
        2 = 2 * b 2n +(a n-1 2n-3 - a n < / sup> 2n-3 )=
        5 = 2 * b 2n - b n

      这些规则是确切的序列定义,并提供了一种在线性时间内生成n集的方法(与依次生成每个集时的二次方相对)

答案 1 :(得分:1)

集合中的最小数字似乎是Narayana-Zidek-Capell numbers

1, 1, 2, 3, 6, 11, 22, ...

通过反向重复添加这些数字,从第一个数字中获得其他数字。

例如,

S6 = {11, 17, 20, 22, 23, 24}
        +6  +3  +2  +1  +1

使用该链接中找到的Narayana-Zidek-Capell序列的重复,我已设法为O(n)时间内运行的此问题生成解决方案。这是Java的解决方案。由于n <= 32溢出,它仅适用于int,但可以使用BigInteger来编写更高的值。

static Set<Integer> set(int n) {
    int[] a = new int[n + 2];
    for (int i = 1; i < n + 2; i++) {
        if (i <= 2)
            a[i] = 1;
        else if (i % 2 == 0)
            a[i] = 2 * a[i - 1];
        else
            a[i] = 2 * a[i - 1] - a[i / 2];
    }
    Set<Integer> set = new HashSet<>();
    int sum = 0;
    for (int i = n + 1; i >= 2; i--) {
        sum += a[i];
        set.add(sum);
    }
    return set;
}

我现在无法证明为什么这与问题中的设置相同,但我正在研究它。但是我检查了所有n <= 32这个算法给出了与“明显”算法相同的集合,所以我有理由相信它是正确的。