【问题标题】:Finding nearest non-coprime number寻找最近的非互质数
【发布时间】:2016-11-19 22:06:57
【问题描述】:

给定一个数组,我需要找到最近的非互质数的索引(即 GCD(Ai, Aj) > 1 ,对于数组中的任何 Ai 和 Aj,i != j )示例,让数组为

[2 17 4 6 10]

答案是

[3 -1 4 3 4] 

我使用二进制 GCD 方法编写了这个蛮力代码(O(n^2)),效率不高。我想知道是否有更快的方法来做到这一点。 尤其是 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;
        }
    }
}

【问题讨论】:

  • 我投票结束这个问题,因为它属于Code Review
  • @JimGarrison 它也是标记算法,它不仅仅是 OP 想要建议的代码。
  • OP 提供了示例代码——我们要求人们这样做——但问题是关于算法,而不是关于代码。我们可以利用如果 gcd(a,b *c) > 1 如果 gcd(a,b)>1 或 gcd(a,c) > 1 的事实吗?我听说过在因式分解算法中使用了这个技巧,但是看看这些数字,我看不出它是如何在这里得到回报的——gcd 的成本似乎上升得太快,因为要分解的数字中的位数增加。如果我错了,它可能有助于生成一棵二叉树,其中节点处的数字是其下方叶子中所有数字的乘积。
  • @JimGarrison 虽然我同意这个问题不适合 Stack Overflow,但选择help center 中实际说明的关闭原因会更有用,这可以解释 为什么 不适合堆栈溢出。 (也就是说,要求提高性能并不是要解决的具体问题。)
  • @mcdowella 如果我生成这样一棵树,在每个索引之前计算产品,那么这个数字不会增长很快吗?我不是很擅长数论,但是如果我用质数对其进行修改,它会保留信息吗?

标签: java algorithm


【解决方案1】:

如果你知道你的数字的最大值并且有能力保留一个素数列表,那么将它们分解可能是一个更好的解决方案对于平均/随机情况。否则,最坏情况的复杂度仍然是 O(N*N) - 在最坏的情况下认为“所有这些都是素数”。

方法:

  • 分解它们并存储Map&lt;prime, multiplicity&gt;[N] + int closestNeigh[]
  • 取一个因子,O(N) 为每个因子确定包含该因子的最接近的值(将涉及前缀/后缀总和)
  • 从所有因子图中消除该因子
  • 采用下一个因素。仅当新的最近邻索引最接近时才调整最近邻索引。

这可能会在O(N*&lt;num_distict_factors&gt;) 的行上带来一些“救济”,但同样如果&lt;num_distict_factors&gt; == N(所有素数),那么它仍然是 O(N*N)

【讨论】:

  • 我想出了一个类似的方法,我维护了 N 个索引列表,其中 N 是素数(每个素数 1 个列表)。然后对于每个索引,我在 X 列表(当前数字的素数分解中存在的列表)中二进制搜索最近的索引,但它仍然很慢。
  • @MayurKulkarni “但它仍然很慢。”正如我所说,我相信最坏的情况仍然是 O(N*N)。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 1970-01-01
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    相关资源
    最近更新 更多