如何在阵列中找到比O(N ^ 2)更快的毕达哥拉斯三胞胎?

时间:2010-01-09 02:42:30

标签: algorithm

有人可以提出一种算法,在给定数组中的数字中查找所有毕达哥拉斯三元组吗?如果可能,请建议比O(n 2 )更快的算法。

毕达哥拉斯三元组是一组{a,b,c},使得 2 = b 2 + c 2 < / SUP>。示例:对于数组[9, 2, 3, 4, 8, 5, 6, 10],算法的输出应为{3, 4, 5}{6, 8, 10}

17 个答案:

答案 0 :(得分:31)

我理解这个问题是

  

给定一个数组,找到所有这些三元组ijk,这样a [i] 2 = a [j] 2 + A [k]的 2

解决方案的主要思想是:

  • 对每个元素进行平方。 (这需要O(n)时间)。这将把原始任务减少为“在数组中找到三个数字,其中一个是其他两个数字的总和”。

现在你知道如何在低于O(n 2 )的时间内解决这样的任务,使用这样的算法。我的想法只有以下O(n 2 )解决方案:

  1. 按升序对数组进行排序。这需要O(n log n)。
  2. 现在考虑每个元素a [i]。如果a [i] = a [j] + a [k],那么,由于数字是正的并且现在对数组进行排序,因此k

    要查找此类索引,请运行一个将j1增加到i的循环,并将ki减少到0同时,直到他们见面。如果j增加a[j]+a[k] < a[i],如果总和大于k则减少a[i]。如果总和相等,那就是答案之一,打印它,并移动两个索引。

    这需要O(i)操作。

  3. 对每个索引i重复步骤2。这样你就完全需要O(n 2 )操作,这将是最终的估计。

答案 1 :(得分:11)

对于密切相关的3SUM问题(http://en.wikipedia.org/wiki/3SUM),没有人知道如何做得比二次方显着更好。我认为快速解决问题的可能性不太可能。


3SUM问题是找到a + b + c = 0.当输入是真正的代数数时,让PYTHTRIP成为找到^ 2 + b ^ 2 = c ^ 2的问题。这是从3SUM到PYTHTRIP的O(n log n)时间减少。正如ShreevatsaR指出的那样,这并不排除数论理论的可能性(或3SUM的解决方案!)。

首先我们将3SUM减少到一个我称之为3SUM-ALT的问题。在3SUM-ALT中,我们想要找到一个+ b = c,其中所有数组条目都是非负的。从3SUM-ALT到PYTHTRIP的完成减少只是取得了根源。

要使用3SUM-ALT求解3SUM,首先消除a,b,c之一为零(O(n log n))的三元组的可能性。现在,任何满意的三元组都有两个正数和一个负数,或者两个负数和一个正数。设w是大于任何输入数的绝对值的三倍的数。求解3SUM-ALT的两个实例:其中所有负x映射到w-x,所有正x映射到2w + x;一个所有负x映射到2w-x而所有正x映射到w + x。证明的其余部分很简单。

答案 2 :(得分:5)

我还有一个解决方案,

//sort the array in ascending order 
//find the square of each element in the array

//let 'a' be the array containing square of each element in ascending order 

for(i->0 to (a.length-1))
  for (j->i+1 to  (a.length-1))
    //search the a[i]+a[j] ahead in the array from j+1 to the end of array
      //if found get the triplet according to sqrt(a[i]),sqrt(a[j]) & sqrt(a[i]+a[j])
  endfor
endfor

答案 3 :(得分:4)

不确定这是否更好,但你可以通过计算所有可能的小于或等于它的三元组来计算它们与列表中的最大值成比例的时间。以下Perl代码可以。算法的时间复杂度与最大值成比例,因为反平方和的总和1 + 1 ^ 2 ^ 2 + 1/3 ^ 3 ....等于Pi ^ 2/6,一个常数。

我刚刚使用Wikipedia页面中的公式来生成没有唯一的三元组。

my $list = [9, 2, 3, 4, 8, 5, 6, 10];
pythagoreanTriplets ($list);

sub pythagoreanTriplets
{
  my $list = $_[0];
  my %hash;
  my $max = 0;
  foreach my $value (@$list)
  {
    $hash{$value} = 1;
    $max = $value if ($value > $max);
  }
  my $sqrtMax = 1 + int sqrt $max;

  for (my $n = 1; $n <= $sqrtMax; $n++)
  {
    my $n2 = $n * $n;
    for (my $m = $n + 1; $m <= $sqrtMax; $m++)
    {
      my $m2 = $m * $m;
      my $maxK = 1 + int ($max / ($m2 + $n2));
      for (my $k = 1; $k <= $maxK; $k++)
      {
        my $a = $k * ($m2 - $n2);
        my $b = $k * (2 * $m * $n);
        my $c = $k * ($m2 + $n2);
        print "$a $b $c\n" if (exists ($hash{$a}) && exists ($hash{$b}) && exists ($hash{$c}));
      }
    }
  }
}

答案 4 :(得分:4)

这是一个解决方案,可以更好地扩展大型小数字列表。至少它是不同的; v)。

根据http://en.wikipedia.org/wiki/Pythagorean_triple#Generating_a_triple

a = m^2 - n^2, b = 2mn, c = m^2 + n^2 

b看起来不错,嗯?

  • 在O(N log N)时间内对数组进行排序。
  • 对于每个元素b,找到素数因子分解。天真使用素数表直到最大输入值M的平方根,每个元素需要O(sqrt M/log M)时间和空间*。
  • 对于每对(m,n), m > n, b = 2mn(跳过奇数b),在已排序的数组中搜索m^2-n^2m^2+n^2。每对O(log N),每个元素O(2^(Ω(M))) = O(log M) **对,O(N(log N)(log M))总数。

最终分析:O(N((sqrt M / log M)+(log N * log M))),N =数组大小,M =值的大小。

(*要接受64位输入,大约有203M 32位素数,但我们可以使用每个素数one byte处的差异表,因为差异都是均匀的,也可能产生大的差异按需接收质数。为了接受32位输入,需要一个16位素数的表,这个表足够小以适应L1高速缓存。假设所有素因子都小于平方根,这里的时间过高估计。 )

(**由于重复的素因子,实际界限较低。)

答案 5 :(得分:2)

我的一些同事在一个java证书课程中被问到同样的问题他们正在采取我们提出的解决方案是O(N ^ 2)。我们尽可能地减少了问题空间,但我们找不到将复杂性降低到N Log N或更好的方法。

    public static List<int[]> pythagoreanTripplets(int[] input) {
    List<int[]> answers = new ArrayList<int[]>();
    Map<Long, Integer> map = new HashMap<Long, Integer>();

    for (int i = 0; i < input.length; i++) {
        map.put((long)input[i] * (long)input[i], input[i]);
    }

    Long[] unique = (Long[]) map.keySet().toArray(new Long[0]);
    Arrays.sort(unique);
    long comps =0;
    for(int i =  1 ; i < unique.length;i++)
    {
        Long halfC = unique[i]/2;
        for(int j = i-1 ; j>= 0 ; j--)
        {

            if(unique[j] < halfC) break;
            if(map.containsKey(unique[i] - unique[j]))
            {
                answers.add(new int[]{map.get(unique[i] - unique[j]),map.get(unique[j]),map.get(unique[i])});
            }
        }
    }
    return answers;
}

答案 6 :(得分:2)

O(N)中的解决方案。

  1. 找出数组中的最小元素。 min O(n)。
  2. 找出数组中的最大元素。 max O(n)。
  3. 制作一个hastable元素,以便可以在O(1)中搜索元素。
  4. m ^ 2-1 = min ....从步骤1中取出min。在这个等式中找出m.O(1)
  5. 2m = min ....从步骤1开始min。在这个等式中找出m.O(1)
  6. m ^ 2 + 1 = max ....从步骤2中取出最大值。在此等式中找出m .O(1)

  7. 选择(步骤4,5,6)的最小楼层,让我们说minValue.O(1)

  8. 选择最大的ceil(步骤4,5,6)让我们说maxValue.O(1)

  9. 从j = minValue循环到maxValue。 maxvalue-minvalue将小于N的根。 9.a计算三个数字j ^ 2-1,2j,j ^ 2 + 1。 9.b在哈希表中搜索这些数字。如果发现返回成功。

  10. 返回失败。

答案 7 :(得分:1)

这是我实施的那个......

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 
 * @author Pranali Choudhari (pranali_choudhari@persistent.co.in)
 */
public class PythagoreanTriple {

/
    //I hope this is optimized



    public static void main(String[] args) {

        Map<Long,Set<Long>> triples = new HashMap<Long,Set<Long>>();
        List<Long> l1 = new ArrayList<Long>();
        addValuesToArrayList(l1);
        long n =0;        
        for(long i : l1){
            //if its side a.
             n = (i-1L)/2L;
             if (n!=0 && n > 0){
                  putInMap(triples,n,i);
                  n=0;
             }
             //if its side b 

             n = ((-1 + Math.round(Math.sqrt(2*i+1)))/2);
             if (n != 0 && n > 0){
                 putInMap(triples,n,i);
                  n=0;
             }
             n=  ((-1 - Math.round(Math.sqrt(2*i+1)))/2);
             if (n != 0 && n > 0){
                 putInMap(triples,n,i);
                  n=0;
             }
             //if its side c

             n = ((-1 + Math.round(Math.sqrt(2*i-1)))/2);
             if (n != 0 && n > 0){
                 putInMap(triples,n,i);
                  n=0;
             }
             n=  ((-1 - Math.round(Math.sqrt(2*i-1)))/2);
             if (n != 0 && n > 0){
                 putInMap(triples,n,i);
                  n=0;
             }


        }
        for(Map.Entry<Long, Set<Long>> e : triples.entrySet()){
            if(e.getValue().size() == 3){
                System.out.println("Tripples" + e.getValue());
            }
            //need to handle scenario when size() > 3 
            //even those are tripples but we need to filter the wrong ones
        }


    }

    private static void putInMap( Map<Long,Set<Long>> triples, long n,  Long i) {
        Set<Long> set = triples.get(n);
        if(set == null){
            set = new HashSet<Long>();
            triples.put(n, set);
        }
        set.add(i);
    }

    //add values here 
    private static void addValuesToArrayList(List<Long> l1) {
        l1.add(1L);
        l1.add(2L);
        l1.add(3L);
        l1.add(4L);
        l1.add(5L);
        l1.add(12L);
        l1.add(13L);

    }
}

答案 8 :(得分:1)

可以在O(n)时间内完成。首先在地图中散列元素以进行存在检查。之后应用以下算法

扫描数组,如果元素是偶数,则(n,n ^ 2/2 + 1,n ^ 2/2 -1)是三元组。使用哈希映射查找检查是否存在。如果三元组中的所有元素都存在,则打印三元组。

答案 9 :(得分:1)

如果(a,b,c)是毕达哥拉斯三重,则任何正整数都是(ka,kb,kc)。

只需为a,b和c找到一个值,然后就可以根据需要计算多个新值。

伪代码:

a = 3
b = 4
c = 5
for k in 1..N:
  P[k] = (ka, kb, kc)

请告诉我这是不是您正在寻找的内容。

答案 10 :(得分:0)

这是Java中的实现:

/**
 * Step1: Square each of the elements in the array [O(n)]
 * Step2: Sort the array [O(n logn)]
 * Step3: For each element in the array, find all the pairs in the array whose sum is equal to that element [O(n2)]
 * 
 * Time Complexity: O(n2) 
 */
public static Set<Set<Integer>> findAllPythogoreanTriplets(int [] unsortedData) {

    // O(n) - Square all the elements in the array
    for (int i = 0; i < unsortedData.length; i++)
        unsortedData[i] *= unsortedData[i];

    // O(n logn) - Sort
    int [] sortedSquareData = QuickSort.sort(unsortedData);

    // O(n2)
    Set<Set<Integer>> triplets = new HashSet<Set<Integer>>();

    for (int i = 0; i < sortedSquareData.length; i++) {

        Set<Set<Integer>> pairs = findAllPairsThatSumToAConstant(sortedSquareData, sortedSquareData[i]);

        for (Set<Integer> pair : pairs) {
            Set<Integer> triplet = new HashSet<Integer>();
            for (Integer n : pair) {
                triplet.add((int)Math.sqrt(n));
            }
            triplet.add((int)Math.sqrt(sortedSquareData[i])); // adding the third element to the pair to make it a triplet
            triplets.add(triplet);
        }
    }

    return triplets;
}


public static Set<Set<Integer>> findAllPairsThatSumToAConstant(int [] sortedData, int constant) {

    // O(n)
    Set<Set<Integer>> pairs = new HashSet<Set<Integer>>();
    int p1 = 0; // pointing to the first element
    int p2 = sortedData.length - 1; // pointing to the last element
    while (p1 < p2) {
        int pointersSum = sortedData[p1] + sortedData[p2];
        if (pointersSum > constant)
            p2--;
        else if (pointersSum < constant)
            p1++;
        else {
            Set<Integer> set = new HashSet<Integer>();
            set.add(sortedData[p1]);
            set.add(sortedData[p2]);
            pairs.add(set);
            p1++;
            p2--;
        }
    }
    return pairs;
}

答案 11 :(得分:0)

如果问题是那个&#34;对于一个整数数组,找到所有三元组,使得a ^ 2 + b ^ 2 = c ^ 2

将数组按升序排序

在条目0,1,2处设置三个指针p1,p2,p3 将pEnd设置为超过数组中的最后一个条目

while(p2&lt; pend-2) {

sum = (*p1 * *p1 + *p2 * *p2)


while ((*p3 * *p3) < sum && p3 < pEnd -1)
   p3++;

if ( *p3 == sum) 
   output_triple(*p1, *p2, *p3);

p1++;
p2++;

}

它在数组上移动3个指针,因此它为O(sort(n)+ n) 它不是n2,因为下一个传递从下一个最大数字开始并且不会重置。 如果最后一个数字对于三元组而言太小,那么当你去下一个更大的a和b时,它仍然很小

答案 12 :(得分:0)

public class FindPythagorusCombination {

    public static void main(String[] args) {
        int[] no={1, 5, 3, 4, 8, 10, 6 };
        int[] sortedno= sorno(no);
        findPythaComb(sortedno);
    }

    private static void findPythaComb(int[] sortedno) {
        for(int i=0; i<sortedno.length;i++){

            int lSum=0, rSum=0;
            lSum= sortedno[i]*sortedno[i];
            for(int j=i+1; j<sortedno.length; j++){
                for(int k=j+1; k<sortedno.length;k++){
                    rSum= (sortedno[j]*sortedno[j])+(sortedno[k]*sortedno[k]);
                    if(lSum==rSum){
                        System.out.println("Pythagorus combination found: " +sortedno[i] +" " +sortedno[j]+" "+sortedno[k]);
                    }else
                        rSum=0;
                }

            }
        }
    }

    private static int[] sorno(int[] no) {

        for(int i=0; i<no.length;i++){

            for(int j=i+1; j<no.length;j++){
                if(no[i]<no[j]){
                    int temp= no[i];
                    no[i]= no[j];
                    no[j]=temp;
                }
            }

        }

        return no;

    }



}

答案 13 :(得分:0)

    import java.io.*;
import java.lang.*;
import java.util.*;

class PythagoreanTriplets
{
 public static void main(String args[])throws IOException
 {
  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

  int n = Integer.parseInt(br.readLine());
  int arr[] = new int[n];
  int i,j,k,sum;
  System.out.println("Enter the numbers ");
  for(i=0;i<n;i++)
   {
    arr[i]=Integer.parseInt(br.readLine());
    arr[i]=arr[i]*arr[i];
   }
Arrays.sort(arr);
  for(i=n-1;i>=0;i--)
   {
     for(j=0,k=i-1;j<k;)
        {  
          sum=arr[j]+arr[k];
          if(sum==arr[i]){System.out.println((int)Math.sqrt(arr[i]) +","+(int)Math.sqrt(arr[j])+","+(int)Math.sqrt(arr[k]));break;}
          else if(sum>arr[i])k--;
          else j++;

        }
   }

}
}

答案 14 :(得分:0)

看看我编写的以下代码。

php artisan config:cache

答案 15 :(得分:0)

毕达哥拉斯三重奏的柏拉图公式: 希腊哲学家柏拉图(Plato)提出了一个找到毕达哥拉斯三元组的好方法。

(2m)^2 + (m^2 - 1)^2 = (m^2 + 1)^2

bool checkperfectSquare(int num){
    int sq=(int)round(sqrt(num));
    if(sq==num/sq){
        return true;
    }
    else{
        return false;
    }
}

void solve(){
    int i,j,k,n;

    // lenth of array
    cin>>n;
    int ar[n];
    // reading all the number in array
    for(i=0;i<n;i++){
        cin>>ar[i];
     }
    // sort the array
    sort(ar,ar+n);
    for(i=0;i<n;i++){
        if(ar[i]<=2){
            continue;
        }
        else{
            int tmp1=ar[i]+1;
            int m;
            if(checkperfectSquare(tmp1)){
                m=(ll)round(sqrt(tmp1));
                int b=2*m,c=(m*m)+1;
                if(binary_search(ar,ar+n,b)&&binary_search(ar,ar+n,c)){
                    cout<<ar[i]<<" "<<b<<" "<<c<<endl;
                    break;
                }
            }
            if(ar[i]%2==0){
                m=ar[i]/2;
                int b=(m*m-1),c=(m*m+1);
                if(binary_search(ar,ar+n,b)&&binary_search(ar,ar+n,c)){
                    cout<<ar[i]<<" "<<b<<" "<<c<<endl;
                    break;
                }
            }
        }
    }
}


答案 16 :(得分:-1)

在O(n)中找到毕达哥拉斯三胞胎

算法:

  1. 对于数组中的每个元素,检查它是否为素数
  2. 如果是素数,则计算其他两个数字为((n ^ 2)+1)/ 2和((n ^ 2)-1)/ 2并检查这两个计算出的数字是否在数组中
  3. 如果不是素数,则计算其他两个数字,如下面给出的代码中的其他情况所述
  4. 重复直到到达阵列末尾

     int arr[]={1,2,3,4,5,6,7,8,9,10,12,13,11,60,61};
     int prim[]={3,5,7,11};//store all the prime numbers
     int r,l;
     List<Integer> prime=new ArrayList<Integer>();//storing in list,so that it is easy to search
     for(int i=0;i<4;i++){
      prime.add(prim[i]);
     }
     List<Integer> n=new ArrayList<Integer>();
     for(int i=0;i<arr.length;i++)
     {
            n.add(arr[i]);
     }
     double v1,v2,v3;
     int dummy[]=new int[arr.length];
     for(int i=0;i<arr.length;i++)
        dummy[i]=arr[i];
    
     Integer x=0,y=0,z=0;
     List<Integer> temp=new ArrayList<Integer>();
     for(int i=0;i<arr.length;i++)
     {
         temp.add(arr[i]);
     }
    
     for(int j:n){
        if(prime.contains(j)){//if it is prime
            double a,b; 
            v1=(double)j;
            v2=Math.ceil(((j*j)+1)/2);
            v3=Math.ceil(((j*j)-1)/2);
            if(n.contains((int)v2) && n.contains((int)v3)){
              System.out.println((int)v1+" "+(int)v2+" "+(int)v3);
            }
        }
        else//if it is not prime
        {
             if(j%3==0){
                x=j;
                y=4*(j/3);
                z=5*(j/3);
                if(temp.contains(y) && temp.contains(z)){
                        System.out.println(x+" "+y+" "+z);
                        //replacing those three elements with 0
                        dummy[temp.indexOf(x)-1]=0;
                        dummy[temp.indexOf(y)-1]=0;
                        dummy[temp.indexOf(z)-1]=0;
                }
             }   
        }//else end
    }//for end
    
  5. 复杂性:O(n)