【问题标题】:Recursive find order of square sums with given length给定长度的平方和的递归查找顺序
【发布时间】:2021-02-02 21:51:13
【问题描述】:

编写函数,给定一个整数 N,返回整数数组 1..N 以某种方式排列,因此每 2 个连续数字的总和是一个平方。

当且仅当满足以下两个条件时,解决方案才有效:

1..N 范围内的每个数字都使用一次且仅一次。

每两个连续数之和是一个完美平方(根平方)。

结果是按照上述规则按特定顺序排列的 1-n 个数字的列表!!!

示例 对于 N=15,解决方案可能如下所示:

[9、7、2、14、11、5、4、12、13、3、6、10、15、1、8]

检查-

   16    16     16     16     16     16     16
   /+\   /+\    /+\    /+\    /+\    /+\    /+\
[ 9, 7, 2, 14, 11, 5, 4, 12, 13, 3, 6, 10, 15, 1, 8 ]
      \+/    \+/    \+/    \+/    \+/    \+/    \+/
       9     25      9     25      9     25      9


9 = 3*3
16 = 4*4
25 = 5*5

length of the returned list is n=15 and uses numbers from 1-n(15)

如果没有解,返回null 例如如果 N=5,则数字 1,2,3,4,5 不能放入平方和行:1+3=4, 4+5=9,但是 2 没有对,不能链接 [1,3 ] 和 [4,5]。

解决方案列表返回必须使用不超过 n 的数字,并且长度必须等于 n

我的解决方案是使用递归方法,这是我的尝试:

import java.util.*;

public class SquareSums {
   public static List<Integer> gg= new ArrayList<Integer>() ;

    public static List<Integer> buildUpTo(int n) {
        int checkCurrent=1,firstInList=1;
        while(checkCurrent<=n+1){
          boolean[] flag= new boolean[n+1];
          List<Integer> l= new ArrayList<Integer>();
          l=checker(n,checkCurrent,l,1,flag,firstInList, true);//n, prev, list, current, flag
          checkCurrent++;
          if(l.size()==n) {System.out.println("true "+ n);return l;}
        }
         return null;
}
  public static List<Integer> checker(int num, int current,List<Integer> l,int prev, boolean[] flag,int k, boolean one){
      if(l.size()==num) {return l;}
      else if(prev>num+1 || current>num) {return l;}
      if(one) {
          flag[current]=true;
          l.add(current);print(l);
          l=checker(num,1,l,current,flag,1,false);
      }
          flag[prev]=true;
          System.out.println("check if square root: "+" num1: "+prev+ " num2: "+current+" sum is: "+(prev+current)+" sqrt: "+Math.sqrt(prev+current)+ " sqrt int: "+Math.floor(Math.sqrt(prev+current)));

          if(current<flag.length &&!flag[current] && Math.sqrt(prev+current)==Math.floor(Math.sqrt(prev+current))) {
            l.add(current);print(l);
            l=checker(num,1,l,current,flag,1,false);
          }
          else {
              print(l);
              l=checker(num,k++,l,prev,flag,k,false); 
          }
    return l;
  }
  
  public static void main(String[] args) {
      List<Integer> l= buildUpTo(15);
      print(l);
      }
  static void print(List<Integer> l) {
      System.out.println(" -------------------- ");
      for(int i=0;i<l.size();i++) {
          System.out.println(l.get(i));
      }
      System.out.println(" --------------------- ");
      System.out.println(" ");
      System.out.println(" ");


  }
  }

虽然我认为它失败了,但原因如下:

我如何解决/修复代码以使其正常工作(使用递归方法&没有流?)?

这里是测试代码:

import org.junit.Test;
import org.junit.runners.JUnit4;
import java.util.List;

public class Tests {
    
    @Test
    public void sampleTests() {
        
        List.of(5,15,16,23,37).forEach(Check::isGood); 
    }
}

编辑:

然后在下面发布的代码解决方案的帮助下,我对此进行了一些编辑,但它仍然无法正常工作。

public class SquareSums {
   public static List<Integer> gg= new ArrayList<Integer>() ;

    public static List<Integer> buildUpTo(int n) {
        int checkCurrent=1;
        while(checkCurrent<=n+1){
          List<Integer> l=checker(n,checkCurrent,new ArrayList<Integer>(),1,new boolean[n+1],1);//n, prev, list, current, flag
          checkCurrent++;
          if(l.size()==n) {System.out.println("true "+ n);return l;}
        }
         return gg;
         
}
  static boolean checkPerfectSquare(double x)
  {
      double sq = Math.sqrt(x);
      return ((sq - Math.floor(sq)) == 0);
  }
  public static List<Integer> checker(int num, int current,List<Integer> l,int prev, boolean[] flag,int k){
      if(l.size()==num) {return l;}
      else if(prev>num+1 || current>num) {return l;}
      if(l.size()==0) {
          flag[current]=true;
          l.add(current);System.out.println(l);
          l=checker(num,1,l,current,flag,1);
      }
          flag[prev]=true;
          if(current<flag.length &&!flag[current] && checkPerfectSquare(prev+current)) {
            l.add(current);System.out.println(l);
            l=checker(num,1,l,current,flag,1);
            l.remove(l.size()-1);
            flag[l.size()-1]=false;
          }
          else {
              System.out.println(l);
              l=checker(num,k++,l,prev,flag,k); 
          }
    return l;
  }
  
  public static void main(String[] args) {
      List<Integer> l= buildUpTo(15);
      System.out.println("\n"+"Answer is: ");
      System.out.println(" -------------------- ");
      System.out.println(l);
      System.out.println(" -------------------- ");

      }
  }

我运行了代码,当我在示例 n=15 上运行它时,它假设返回一个以 9 开​​头的列表,在我的调试器上/在 9 上打印它打印

[9, 7, 2, 14, 11, 5, 4, 12, 13, 3, **1, 8**]

而预期的实际结果是:(不是我的代码,n=15时的真正解决方案)

[9, 7, 2, 14, 11, 5, 4, 12, 13, 3, **6, 10, 15, 1, 8**]

如果我们仔细观察,我们会发现我的代码哪里出错了。似乎我需要在递归中添加一些东西来尝试每个选项。我该怎么做?

【问题讨论】:

标签: java recursion


【解决方案1】:

这个问题可以改写为:

  1. 求每两个连续数之和为完美平方(平方根)的整数的最大序列 (1..N)。
  2. 如果存在,最长的序列是数组本身的大小,这意味着每个整数都会出现在结果中。如果最长的不是数组本身的大小,则返回空。

另外一个观察是:如果序列存在,必然有一对结果,因为如果result [0..n-1]是结果,那么逆序列[n-1 ..0]也是一个结果。例如:

[9, 7, 2, 14, 11, 5, 4, 12, 13, 3, 6, 10, 15, 1, 8]

[8, 1, 15, 10, 6, 3, 13, 12, 4, 5, 11, 14, 2, 7, 9]

都是正确答案。

现在找到整数的最大序列(1..N),每 2 个连续数字之和是一个完美平方(平方根),这听起来对其他最大序列算法问题非常熟悉。而DFS(深度优先搜索)可以用于这个问题,如果你把1到n作为一个图表。 (或者你可以称之为回溯算法)

murari99732 提供了一个使用 DFS 的解决方案,但是当 arr 为 0 时只有一个小问题

if (ar.size() == 0) {
            ar.add(i);
            check[i] = true;
            getvalue(n+1,size, ar,range, check);
            check[i] = false;
        }

访问后该项目应重置,代码应为:

   if (ar.size() == 0) {
                ar.add(i);
                check[i] = true;
                getvalue(n+1,size, ar,range, check);
                ar.remove(ar.size()-1);
                check[i] = false;
            }

这是我使用 DFS 的完整代码:

import java.util.ArrayList;
import java.util.List;

public class Test {


    private boolean checkPerfectSquare(double x)
    {
        double sq = Math.sqrt(x);
        return ((sq - Math.floor(sq)) == 0);
    }

    private void solution(int current, int size, List<Integer> ar, boolean check[]){

        if(current==size)
        {
            System.out.println(ar);
            return;
        }
        for (int i = 1; i <=size; i++) {
            if (ar.size() == 0) {
                check[i] = true;
                ar.add(i);
                solution(current+1,size, ar, check);
                ar.remove(ar.size()-1);
                check[i] = false;
            }
            else if(check[i]==false)
            {
                int val=ar.get(ar.size()-1);
                if(checkPerfectSquare(val+i))
                {
                    check[i]=true;
                    ar.add(i);
                    solution(current+1,size, ar, check);
                    ar.remove(ar.size()-1);
                    check[i]=false;
                }
            }
        }
    }

    public static void main(String[] args){

        Test test = new Test();

        test.solution(0,15, new ArrayList<>(),new boolean[16]);

    }
}

【讨论】:

  • 我该如何优化代码,所以如果我尝试在 n 超过 1000 时运行它以最小化运行时间?
  • 很遗憾,回溯算法的时间复杂度很差,最坏的情况是 O(n!)。对于这个,我只能想到如果找到一个解决方案就立即返回,因此它不需要计算所有解决方案。
  • 提高时间效率的更好方法是什么?
【解决方案2】:

我已经实现了可以为您提供特定范围内的连续平​​方和序列的代码。

所以你可以根据你的要求修改它。

  public static void main(String[] args) {
        //n -> 0 will be starting point
        // size-> 15 size of sequence
        //range-> range for which you want to find. Example value should include between 1 to 30
        //check-> so that now value should be repeated so

        //if you want to repeat value just remove boolean flag it will work and put all if else into one code
        getvalue(0,15, new ArrayList<>(),20,new boolean[20]);
    }
    static void getvalue(int n, int size, ArrayList<Integer> ar, int range, boolean check[]) {
        if(n==size)
        {
            System.out.println(ar);
            return;
        }
        for (int i = 1; i < range; i++) {
            if (ar.size() == 0) {
                ar.add(i);
                check[i] = true;
                getvalue(n+1,size, ar,range, check);
                check[i] = false;
            }
            else if(check[i]==false)
            {
                int val=ar.get(ar.size()-1);
                if(checkPerfectSquare(val+i))
                {
                    check[i]=true;
                    ar.add(i);
                    getvalue(n+1,size, ar,range, check);
                    ar.remove(ar.size()-1);
                    check[i]=false;
                }
            }
        }
    }
    static boolean checkPerfectSquare(double x)
    {
        double sq = Math.sqrt(x);
        return ((sq - Math.floor(sq)) == 0);
    }

你也可以在这里查看-> https://github.com/murari99732/solutionleetcode-adventofcode/blob/master/PracticeAlgo/src/ConsecutiveSquare.java

【讨论】:

  • yours 总是返回一个以 1 开头的列表,如果答案列表像上面的示例一样以 9 开​​头呢? (返回列表的长度必须与给定的数字 n 相同)当我在长度为 15(来自示例)和列表以 9 开​​头时运行您的示例时,这就是它返回的内容-[9, 7, 2, 14, 11, 5, 4] 当它应该是[ 9, 7, 2, 14, 11, 5, 4, 12, 13, 3, 6, 10, 15, 1, 8 ]
  • “范围 1..N 中的每个数字都使用一次”,您的代码不会返回每个数字。感谢您的帮助:P
猜你喜欢
  • 1970-01-01
  • 2018-09-01
  • 2022-01-10
  • 2019-09-27
  • 2022-06-23
  • 2013-12-17
  • 1970-01-01
  • 2019-04-21
  • 2019-11-05
相关资源
最近更新 更多