为什么乘以而不是总和

时间:2018-02-16 08:47:59

标签: java arrays math

我需要检查两个数组是否包含相同的数字。所以:

[1,1,2] and [1,2,2] = false;
[2,3,1] and [1,2,3] = true;
[1,2,4] and [1,3,2] = false;
[1,1,4] and [1,2,3] = false;

现在不知怎的,这有效,乘法:

boolean areSimilar(int[] a, int[] b) {
    int s1 = 1, s2 = 1;
    for(int i = 0; i < a.length; i++) {
        s1 *= a[i];
        s2 *= b[i];
    }
    return s1 == s2;
}

但是,如果我将*=替换为+=,那么只需更新一些,[1,1,4] and [1,2,3]将返回true而不是false(显然,因为它将返回6等于6)。

所以我的问题是,我可以确保乘法是故障证明吗? 或者通过乘法可能2个数组可能包含不同的数字但是具有相同的产品?

7 个答案:

答案 0 :(得分:3)

我很确定该产品可能与其中的differenz数字相同。示例:[9 1 2] [18 1 1]

我建议采用不同的解决方案。

首次对阵列进行排序时,您可以检查每个数字是否相同。

答案 1 :(得分:2)

不,乘法不是&#34;故障证明&#34;从这个意义上说。

明显的反例是两个数组都包含 0 (零)作为其元素之一的反例。无论其他元素如何,产品将始终为0。即使没有任何元素为零,也很容易找到更多的反例。

使用乘法只是&#34;安全&#34; 当且仅当数组中的数字是相同数字的prime factors时。素数因子化是唯一的,元素相乘的顺序无关紧要。

编辑:

在这里,我提出了一个解决实际问题的方法,即检查两个数组是否包含相同的元素,忽略顺序。

正如评论中所讨论的那样,虽然它理论上(渐近)慢,但answer by dnswlt中提出的基于排序的方法似乎在实践中效率更高,如果你不关心克隆阵列所暗示的内存消耗。

我最初提出的解决方案具有内存消耗较低,渐近运行时间较短的优点,以及概念适用于其他类型元素的事实(特别是对于包含不可比较的元素的数组,因此无法对其进行排序)。

方法是为每个数组构造一个Map。这个Map将数组的每个元素映射到它出现在数组中的

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SameNumbersInArray
{
    public static void main(String[] args)
    {
        System.out.println(areSimilar(
            new int[] { 1, 1, 2 },
            new int[] { 1, 2, 2 }));

        System.out.println(areSimilar(
            new int[] { 2, 3, 1 },
            new int[] { 1, 2, 3 }));

        System.out.println(areSimilar(
            new int[] { 1, 2, 4 },
            new int[] { 1, 3, 2 }));

        System.out.println(areSimilar(
            new int[] { 1, 1, 4 },
            new int[] { 1, 2, 3 }));

        System.out.println(areSimilar(
            new int[] { 1, 1, 1, 4 },
            new int[] { 1, 1, 4 }));

        System.out.println(areSimilar(
            new int[] { 1, 1, 1, 4 },
            new int[] { 1, 1, 4, 1 }));

    }

    private static boolean areSimilar(int[] a, int[] b) {

        Map<Integer, Long> frequenciesA = IntStream.of(a).boxed()
            .collect(Collectors.groupingBy(
                Function.identity(), Collectors.counting()));
        Map<Integer, Long> frequenciesB = IntStream.of(b).boxed()
            .collect(Collectors.groupingBy(
                Function.identity(), Collectors.counting()));
        return frequenciesA.equals(frequenciesB);
    }

}

测试用例的输出是

false
true
false
false
false
true

但是,对于int[]数组,基于排序的方法在实践中更有效(可能是由于基于计数的方法进行了昂贵的装箱转换)。

答案 2 :(得分:2)

正如其他人所指出的那样,显然既不增加也不增加。典型的基于排序的解决方案如下所示:

boolean areSimilar(int[] a, int[] b) {
    if (a.length != b.length) {
        return false;
    }
    int[] aSorted = a.clone();
    Arrays.sort(aSorted);
    int[] bSorted = b.clone();
    Arrays.sort(bSorted);
    return Arrays.equals(aSorted, bSorted);
}

请注意,我使用clone()来确保您的原始数组保持不变。如果这无关紧要,您可以直接对ab进行排序。

答案 3 :(得分:1)

不,乘法不是故障证明。

考虑数组{1,1,6}和{1,2,3}。它们具有相同的产品(6),但数字不同。

答案 4 :(得分:1)

无法添加乘法。

您正在尝试将N个32位数映射到单个32位数。那就是数据压缩。您不能将32 * N位信息装入32位,就像您不能将N升水放入一升瓶中一样(当然,除非N为1)。

有时,您可以利用数据的某些属性,以便您需要少于32*N位的信息:例如,如果您知道数字介于0和15之间,则每个数字只需要4位。但在一般情况下,你不能。

很简单,总会出现“碰撞”,其中两个不同的数组映射到相同的值。

可以做的是说数字肯定相同。您可以使用乘法或加法方法:如果两个数组的乘积或总和不同,它们肯定不一样;如果是,你必须进一步检查以确定数字是否相同。

这是hashCode方法的基础:正确实施后,hashCode可以确定两个实例是否相等;如果你想知道他们 是否相等,你必须使用equals方法。

答案 5 :(得分:0)

乘法不是故障证明。 这只是一个快速检查。有点像一个简写,找到一个比较是否值得做。校验和(这里检查乘法)类型的检查。

  

[0,x,y,z,...] == [..a,b,0,c ...]总是

答案 6 :(得分:0)

为什么不结合总和和产品? (相当于下面的java代码)

的Scala:

val r = scala.util.Random 
// 10 digits produce to few collisions to control by hand:
// def arr = Vector (r.nextInt (10), r.nextInt (10), r.nextInt (10)) 
def arr = Vector (r.nextInt (3), r.nextInt (3), r.nextInt (3)) 
// first idea: a*b*c + a+b+c, but  3,0,0 and 2,1,0 produce 
// product 0 and sum 3  
// def checksum (v: Vector[Int]) : Int = v.product +  v.sum
// so we just add 1 to every digit 
def checksum (v: Vector[Int]) : Int = (v(0)+1)*(v(1)+1)*(v(2)+1) +  v(0)+v(1)+v(2) 
def cmp (a:Vector[Int], b:Vector[Int]) : Boolean = { checksum(a) == checksum(b)} 
(1 to 30).foreach (i => {val a=arr; val b=arr; val m = cmp (a, b); println (s"match: $a $b $m") })

TESTDATA:

match: Vector(0, 1, 2) Vector(0, 1, 0) false
match: Vector(1, 1, 0) Vector(2, 1, 1) false
match: Vector(2, 1, 0) Vector(2, 0, 2) false
match: Vector(0, 0, 2) Vector(0, 1, 1) false
match: Vector(2, 1, 1) Vector(2, 1, 1) true
match: Vector(0, 1, 1) Vector(1, 0, 2) false
match: Vector(2, 0, 2) Vector(2, 0, 0) false
match: Vector(0, 2, 2) Vector(2, 1, 0) false
match: Vector(2, 2, 2) Vector(0, 1, 2) false
match: Vector(2, 2, 0) Vector(1, 1, 0) false
match: Vector(0, 2, 2) Vector(0, 0, 2) false
match: Vector(0, 2, 1) Vector(2, 1, 1) false
match: Vector(0, 2, 1) Vector(1, 0, 0) false
match: Vector(2, 2, 2) Vector(2, 1, 1) false
match: Vector(0, 1, 1) Vector(1, 0, 2) false
match: Vector(1, 0, 0) Vector(1, 0, 2) false
match: Vector(2, 1, 1) Vector(0, 1, 2) false
match: Vector(2, 0, 0) Vector(2, 1, 0) false
match: Vector(1, 2, 1) Vector(2, 1, 2) false
match: Vector(2, 2, 1) Vector(2, 1, 2) true
match: Vector(0, 0, 0) Vector(0, 2, 2) false
match: Vector(2, 0, 1) Vector(0, 1, 1) false
match: Vector(2, 0, 1) Vector(1, 2, 1) false
match: Vector(0, 1, 1) Vector(1, 1, 0) true
match: Vector(0, 2, 0) Vector(1, 0, 0) false
match: Vector(1, 2, 1) Vector(0, 1, 0) false
match: Vector(2, 0, 2) Vector(0, 1, 0) false
match: Vector(0, 1, 0) Vector(1, 2, 2) false
match: Vector(1, 1, 0) Vector(0, 0, 0) false
match: Vector(0, 1, 1) Vector(0, 2, 1) false

还有很多要控制的情况,I&#39;会编写一个检查功能,在比较之前对Vector进行排序。我想,虽然代码是scala,但它易于理解并转换为java等价?

def cmp2 (a:Vector[Int], b:Vector[Int]) : Boolean = { a.sorted == b.sorted} 
(1 to 3000).foreach (i => {val a=arr; val b=arr; if (cmp (a, b) != cmp2 (a, b)) println (s"match: $a $b ${cmp(a,b)} ${cmp2(a,b)}") })
// silence

当然,你也可以做一个数学证明:

(a+1)*(b+1)*(c+1)+a+b+c ?= (d+a+1)*(e+b+1)*(c+1)+(d+a)+b+c | d!=0
// or
(a+1)*(b+1)*(c+1)+a+b+c ?= (d+a+1)*(e+b+1)*(c+1)+(d+a)+b+c | d!=0, e!=0

作为读者的练习,像往常一样:)

的Java:

Random r = new java.util.Random();

int [] arr (int max) {
  int[] v = new int[3];
  v[0] = r.nextInt (max);
  v[1] = r.nextInt (max);
  v[2] = r.nextInt (max);
  return v;
}

int checksum (int[] v) { return (v[0] +1) * (v[1] +1) * (v[2] +1) + v[0] + v[1] + v[2]; }

boolean cmp (int[] a, int[] b) { return checksum(a) == checksum(b); } 

for (int i = 0; i < 30; ++i) {
  int [] a = arr (3); 
  int [] b = arr (3); 
  boolean m = cmp (a, b); 
  System.out.printf ("match: %6s %6s %b\n",  Arrays.toString (a), Arrays.toString (b), m);
}