在两个数组中找到对,使得当相乘时变成一个完美的正方形
Posted
技术标签:
【中文标题】在两个数组中找到对,使得当相乘时变成一个完美的正方形【英文标题】:Find pairs in two arrays such that when multiplied becomes a perfect square 【发布时间】:2017-10-19 18:20:33 【问题描述】:给定两个这样的整数数组:-
int[] a = 2, 6, 10, 13, 17,18 ; int[] b = 3, 7, 8, 9, 11, 15 ;
我怎样才能从这两个数组中找到对,这样当它们相乘时变成完美的正方形?
例如,在上述数组中,2,8
和 18,8
是两对。
现在我的方法是蛮力,我正在循环遍历两个数组,如下所示:-
int count = 0;
for (int i = 0; i < arr1.Length; i++)
for (int j = 0; j < arr2.Length; j++)
var x = arr1[i] * arr2[j];
long s = (long)Math.Sqrt(x);
if (x == s * s)
count += 1;
我怎样才能有效地做到这一点?
【问题讨论】:
这似乎更像是一场考试/比赛,但我们会尽力提供帮助,因为您提供了代码...... 分解可能吗? @matiaslauriti - 正确,面试问题。我不擅长来自非 CS 背景的算法:( 我不会使用Math.Sqrt()
方法来确定“完美的正方形”。请看this问题。有一个很好的答案解释了一种快速的方法。不过它是 java,但在 C# 世界中移动它应该不是问题。
【参考方案1】:
任何两个数中每个素因数的计数都是偶数,将形成一个有效对。否则,任何具有一个或多个素因数的奇数数只能与另一个具有奇数数的完全相同因数的数配对。这意味着我们需要为每个数字存储的是它的哪个素数具有奇数。这可以被散列。
a = 2, 6, 10, 13, 17,18 ;
b = 3, 7, 8, 9, 11, 15 ;
散列较短的数组,其中键是奇数素数的组合,值是数组中相应的索引列表:
a =
2: [0,5],
2-3: [1],
2-5: [2],
13: [3],
17: [4]
遍历第二个数组,精确匹配prime-factor-with-odd-count-combination:
b[0] -> 3, no match
b[1] -> 7, no match
b[2] -> 2, match with a[0] and a[5] -> 2 * 8 and 18 * 8 are perfect squares
b[3] -> None, no match
b[4] -> 11, no match
b[5] -> 3-5, no match
【讨论】:
@RahulSingh 用于计算素数或之后如何处理散列? 可以使用改良的埃拉托色尼筛来计算素因子。 @גלעדברקן -如果可能,需要通过示例来理解逻辑(也许与我共享的数组有关)。 @RahulSingh 添加了一个示例。 @RahulSingh ehrm 我已经在一天前添加了它的代码,并赞成这个答案。这隐藏了一些复杂性,例如如何分解每个数字?【参考方案2】:对于整数 > 0 的有序(仅对检测最大值)数组,借助改进的 Eratosthenes 筛,我认为复杂度为 O(max * log(max) * log(log(max) ) + n) 并且需要 O(max) 额外空间(与具有 O(1) 空间且不依赖于 max 的标准 O(n^2) 相比,这可能是一个巨大的改进或非常糟糕,具体取决于 n 和 max ):
long total = 0;
int max = (a[a.length - 1] > b[b.length - 1] ? a[a.length - 1] : b[b.length - 1]) + 1;
int[] sieve = new int[max], anti = new int[max], count = new int[max];
for (int i = 1; i < max; i++)
sieve[i] = i; // using numbers and divide by prime instead of booleans
anti[i] = 1; // number by which i has to be multiplied to get the smallest square
for (int i = 2; i < max; i++)
if (sieve[i] == i) // i is prime
for (int j = i; j < max; j += i)
boolean odd = false;
do
odd = !odd;
sieve[j] /= i;
while (sieve[j] % i == 0);
if (odd)
anti[j] *= i;
// if you know the max over all the calls the above has only to be done once
// except for resetting count and total, so we would get O(n) because max is constant
for (int i = 0; i < a.length; i++)
count[anti[a[i]]]++; // hash map for count is better than array if n < max
for (int i = 0; i < b.length; i++)
total += count[anti[b[i]]];
一个平方数将被映射到 1,而其他平方数将映射到数的因式分解中具有奇数指数的素数的乘积。在您的示例中,除了 8 => 2, 9 => 1, 18 => 2 之外,每个数字都映射到自身。18 以下的其他有趣数字(不是正方形或映射到自身):12 => 3。当迭代 a算法在访问 2 时增加 2 的计数一次,在访问 18 时增加一次。当 b 上的迭代访问 8 时,总数将增加 2,这是两个数组之间唯一匹配的结果。
【讨论】:
感谢您的解释。【参考方案3】:我设法使用 LINQ 对其进行了简化:
int[] arr1 = 2, 6, 10, 13, 17, 18 ;
int[] arr2 = 3, 7, 8, 9, 11, 15 ;
int count = arr1.Sum(t => (from t1 in arr2 select t*t1 into x let s = (long) Math.Sqrt(x) where x == s*s select x).Count());
【讨论】:
如果他在面试并且不习惯C#,那么,这个答案对他来说没用,但是LINQ很好 您所做的只是将两个循环转换为 Linq 查询。我们循环遍历每个元素仍然是低效的。 您使用的是数组,当然会有迭代。你无法真正避免它。 linq 不一定是简化。这在很大程度上取决于被视为简单的观点【参考方案4】:我会检查你画完根后是否有小数位。如果没有,那么你就有了完美的平方根:
void Main()
int[] arr1 = 2, 6, 10, 13, 17, 18 ;
int[] arr2 = 3, 7, 8, 9, 11, 15 ;
List<string> PairList = new List<string>();
for (int i = 0; i < arr1.Length; i++)
for (int j = 0; j < arr2.Length; j++)
if (Math.Sqrt(arr1[i] * arr2[j]) % 1 == 0)
PairList.Add(String.Format("0 & 1", arr1[i] , arr2[j]));
PairList.Dump();
【讨论】:
还是O(n*n)
你确定你不应该使用
@DmitryPavlushin 感谢您指出这一点。但它有很大的不同吗?
@RahulSingh 这是真的,它仍然是一个可怕的运行时。以上是关于在两个数组中找到对,使得当相乘时变成一个完美的正方形的主要内容,如果未能解决你的问题,请参考以下文章
当 Python 中的输入是大数时,如何有效地在一个范围内找到完美的正方形