2016-11-19 130 views
1

给定一个数组,我需要找出最近的非互质数的指数(即GCD(Ai,Aj)> 1,对于阵列中的任何Ai和Aj,i!= j)例如,让该阵列是查找最近的非互质数

[2 17 4 6 10] 

答案将是

[3 -1 4 3 4] 

我写此蛮力代码(其是O(n^2)),使用二进制GCD方法,这是不非常有效。我想知道是否有更快的方法来做到这一点。 特别是在O(NlogN)

import java.io.OutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.PrintWriter; 
import java.util.StringTokenizer; 
import java.io.IOException; 
import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.io.InputStream; 

/** 
* Built using CHelper plug-in 
* Actual solution is at the top 
* 
* @author Mayur Kulkarni 
*/ 
public class Main { 
    public static void main(String[] args) { 
     InputStream inputStream = System.in; 
     OutputStream outputStream = System.out; 
     BladeReader in = new BladeReader(inputStream); 
     PrintWriter out = new PrintWriter(outputStream); 
     GCDPuz solver = new GCDPuz(); 
     solver.solve(1, in, out); 
     out.close(); 
    } 

    static class GCDPuz { 
     public static int gcd(int p, int q) { 
      if (q == 0) return p; 
      if (p == 0) return q; 
      // p and q even 
      if ((p & 1) == 0 && (q & 1) == 0) return gcd(p >> 1, q >> 1) << 1; 
       // p is even, q is odd 
      else if ((p & 1) == 0) return gcd(p >> 1, q); 
       // p is odd, q is even 
      else if ((q & 1) == 0) return gcd(p, q >> 1); 
       // p and q odd, p >= q 
      else if (p >= q) return gcd((p - q) >> 1, q); 
       // p and q odd, p < q 
      else return gcd(p, (q - p) >> 1); 
     } 

     public int coprime(int p, int q) { 
      if (p % 2 == 0 && q % 2 == 0) { 
       return 2; 
      } else if (p == q + 1 || q == p + 1) { 
       return 1; 
      } else { 
       return gcd(p, q); 
      } 
     } 

     public void solve(int testNumber, BladeReader in, PrintWriter out) { 
      int size = in.nextInt(); 
      int[] arr = in.readIntArray(size); 
      int[] ans = new int[size]; 
      for (int i = 0; i < arr.length; i++) { 
       if (arr[i] == 1) { 
        ans[i] = -1; 
        continue; 
       } 
       int left = i == 0 ? -1 : findLeft(arr, i); 
       int right = i == arr.length - 1 ? -1 : findRight(arr, i); 
       int leftDist = left == -1 ? -1 : i - left; 
       int rightDist = right == -1 ? -1 : right - i; 
       int anss = findNearestIndex(left, leftDist, right, rightDist); 
       ans[i] = anss == -1 ? -1 : anss + 1; 
      } 
      printa(ans, out); 
     } 

     private void printa(int[] ans, PrintWriter out) { 
      StringBuilder sb = new StringBuilder(); 
      for (int an : ans) { 
       sb.append(an).append(" "); 
      } 
      out.println(sb.toString()); 
     } 

     private int findRight(int[] arr, int i) { 
      if (arr[i] == -1) return -1; 
      for (int j = i + 1; j < arr.length; j++) { 
       if (coprime(arr[i], arr[j]) > 1) return j; 
      } 
      return -1; 
     } 

     private int findLeft(int[] arr, int i) { 
      if (arr[i] == -1) return -1; 
      for (int j = i - 1; j >= 0; j--) { 
       if (coprime(arr[i], arr[j]) > 1) return j; 
      } 
      return -1; 
     } 

     private int findNearestIndex(int one, int oneDist, int two, int twoDist) { 
      if (oneDist == -1 && twoDist == -1) return -1; 
      if (oneDist == -1) return two; 
      if (twoDist == -1) return one; 
      if (oneDist == twoDist) { 
       return Math.min(one, two); 
      } 
      return oneDist < twoDist ? one : two; 
     } 
    } 

    static class BladeReader { 
     public BufferedReader reader; 
     public StringTokenizer tokenizer; 

     public BladeReader(InputStream stream) { 
      reader = new BufferedReader(new InputStreamReader(stream), 32768); 
      tokenizer = null; 
     } 

     public String next() { 
      while (tokenizer == null || !tokenizer.hasMoreTokens()) { 
       try { 
        tokenizer = new StringTokenizer(reader.readLine()); 
       } catch (IOException e) { 
        throw new RuntimeException(e); 
       } 
      } 
      return tokenizer.nextToken(); 
     } 

     public int nextInt() { 
      return Integer.parseInt(next()); 
     } 

     public int[] readIntArray(int size) { 
      int[] array = new int[size]; 
      for (int i = 0; i < size; i++) { 
       array[i] = nextInt(); 
      } 
      return array; 
     } 
    } 
} 
+0

我投票结束这个问题作为题外话,因为它属于[codereview.se] –

+3

@JimGarrison它也被标记算法,它不仅是OP想要建议的代码。 –

+2

OP提供了示例代码 - 我们让人们去做 - 但问题是关于算法的问题,而不是代码。如果gcd(a,b)> 1或gcd(a,c)> 1,那么我们可以利用gcd(a,b * c)> 1的事实吗?我听说过这个技巧被用在因式分解算法中,但是看看这些数字我不明白它在这里可以得到怎样的回报--gCD的成本似乎上升得太快,因为数字中的数字位数被分解了增加。如果我错了,它可能有助于生成一个二叉树,其中节点上的数字是其下面叶子中所有数字的乘积。 – mcdowella

回答

2

如果你知道你的号码你的最大价值,并能负担得起,以保持质数的列表,然后保理他们可能是平均/随机情况下一个更好的解决方案。否则,最坏情况的复杂性,仍然是O(N * N) - 在最坏的情况下认为“所有这些都是质数”。

方法:

  • 因子它们和存储Map<prime, multiplicity>[N] + int closestNeigh[]
  • 采取因子和O(N)确定为每个包含该因子(前缀/ sufix总和将参与)
  • 最接近的
  • 从所有因子图中消除该因子
  • 采取下一个因子。只有最近的邻居索引时,才调整最近的邻居索引。

这可能会带来一些“救济”上的O(N*<num_distict_factors>)行了,但如果重新<num_distict_factors> == N(所有素数),那么它仍然是O(N * N)

+0

我想出了一个类似的方法,我维护了N个索引列表,其中N是素数(每个素数有1个列表)。然后,对于每个索引,我二进制搜索X列表中最接近的索引(列表存在于当前数字的素因式分解中),但它仍然很慢。 –

+0

@MayurKulkarni“但它仍然很慢。”正如我所说,我认为最坏的情况仍然是O(N * N)。 –

0

如果你愿意进入因式分解,可以从左侧遍历列表,将每个数字分解,对每个新素数(以素数为关键字)的索引进行散列,更新已经看到的每个素数的索引,当然,注意最近看过的主要。由于此遍历将错过最近的右侧,因此使用已保存的因子列表从右侧进行另一次遍历,以更新任何更接近的共享素数。