加快N(P)问题搜索-方和问题
我想知道是否有人可以帮助我找出加快我正在从事的小型程序的方法。 问题是一种数学练习,称为和平方问题(https://youtu.be/G1m7goLCJDY)。 因此,您获取了一个从1到N的连续整数列表,看看是否可以对它们进行重新排序,以使每对的总和成为一个平方。 例如:
1-3-6-10-15-
或
1-15-10-6-3-13-12-4-5-11-14-2-7-9-
诀窍是使用域[1,N]中的所有数字。显然,您可以只找到一个证明[1,N]有解决方案的解决方案,或者可以显示该域的所有解决方案。在后一种情况下,显然每个解决方案都会得到一个反向解决方案,此外,您还可以获得所谓的循环解决方案,其中起点和终点也都成平方。 现在增加N(似乎每个N都有23种解决方案-未证明)会以指数方式增加您可以尝试的可能分支或路径的数量。
我的程序首先是简单的循环,它基本上检查了每个选项。这意味着我的计算机(简单的i9内核)在N = 30左右开始挣扎(处理> 1h)。然后,我更改了方法,并首先创建了一个表或矩阵,其中仅包含那些将为每个数字[1,N]创建一个正方形的数字。因此,对于N = 15(第一个可能的N和一个解),您将得到: 对于N = 15,可能的平方是:4,9,16,25
1-3、8、15
2-7、14
3-1、6、13
....
15-1,10
然后我遍历此表以查找可能的解决方案。如果一条路径死路一条,程序将回溯直到有另一个分支,则尝试执行该操作,回溯等。 这花了我大约N = 50的时间才开始花太长时间(> 1h)满足我的口味。 直到那时,我一直在使用Lists(使用C#),所以我尝试了HashSets,使它最多增加了N =70。现在,我似乎陷入了困境。 我要么: 1)必须找到另一个可以更快地处理列表和路径运行的函数,或者 2)在数学上解决此问题时,请考虑一种完全不同的方法 3)提高处理能力
有没有人比我想到的建议更聪明? 我的代码(C#):
using System;
using System.Collections.Generic;
namespace SumSquare
{
public class CalculateHash
{
List<List<int>> matrix = new List<List<int>>();
List<int> squares = new List<int>();
List<int> branch = new List<int>();
HashSet<int> defaultList = new HashSet<int>();
HashSet<int> worklist = new HashSet<int>();
int currentN, currentSet, currentIndex; //, prevIndex;
int max;
List<int> prevIndexSet = new List<int>(); //Holds prevSet index
bool going, going2;
DateTime startTime;
int successes = 0;
int cyclic = 0;
TimeSpan elapsed;
public void Calculate(int maxNum)
{
max = maxNum;
squares = FillSquares(max);
matrix = CreateMatrix(squares, max);
startTime = DateTime.Now;
for (int i = 1; i <= max; i++)
{
branch.Clear();
worklist.Clear();
branch.Add(i);
worklist = FillDefaultList(max);//Fill with all but current start number
worklist.Remove(i);
currentIndex = 0;
currentN = matrix[i][0]; //Get first element
currentSet = i; //remember current set, starting number
going = true;
while (going)
{
if (worklist.Contains(currentN))//Is this number still available?
{
branch.Add(currentN); //add currentN
worklist.Remove(currentN);//Clean up worklist
if (worklist.Count == 0) //All numbers processed! HIT
{
successes++;
ShowSucces();
//to quit at the first success
//going = false; //Quit at first success
//i = max + 1; //quit programm
}
prevIndexSet.Add(currentIndex);
currentSet = currentN;
currentN = matrix[currentSet][0];
currentIndex = 0;//reset for next Set
}
else //number already in use!
{
currentIndex++;
going2 = true;
if (currentIndex == matrix[currentSet].Count) //Used up all the numbers in this set?
{
while (going2)
{
int prevSet = branch[branch.Count - 1];//get the last number added at which this branch halted
branch.RemoveAt(branch.Count - 1); //remove this last number
//We need to add the number prevSet back into the workList BUT
//it needs to go at the correct location!! To keep the numbers in order
worklist.Add(prevSet); //Add the number back
if (branch.Count == 0) //We've exhausted all the possible paths starting with number (i)
{
going2 = false; //break out if this while
going = false; //break out of parent while
}
else
{
currentSet = branch[branch.Count - 1]; //Get the last number of the previous set
currentIndex = prevIndexSet[prevIndexSet.Count - 1] + 1; //Get the position in that set whee we last stopped and get the next position
prevIndexSet.RemoveAt(prevIndexSet.Count - 1);//remove that index reference
if (currentIndex < matrix[currentSet].Count) //No more numbers in this set
{
currentN = matrix[currentSet][currentIndex]; //carry on
going2 = false;
}
}
}
}
else
{
currentN = matrix[currentSet][currentIndex]; //Get the next number in this Set
}
}
}
}
TimeSpan elapsedTime = DateTime.Now - startTime;
Console.WriteLine("Time elapsed: " + elapsedTime.TotalSeconds);
Console.WriteLine("Number of successes: " + (successes * 0.5));
Console.WriteLine("Number of cyclic successes: " + cyclic);
Console.ReadKey();
}
List<List<int>> CreateMatrix(List<int> s, int m)
{
//This creates a 2-dimensional List, First dimension is [1,max], Second dimension the number need to get a sum when added to the current number
List<List<int>> t = new List<List<int>>();
t.Add(new List<int>()); //Element 0 is empty
for (int i = 1; i <= max; i++)
{
List<int> tt = new List<int>();
for (int j = 0; j < s.Count; j++)
{
int difference = s[j] - i;
if ((difference > 0) && (difference <= max) && difference != i) //Needs to be >0 and <=max
{
tt.Add(s[j] - i); //Now add the result: Square - currentINT
}
}
t.Add(tt); //Now stuff it into the parent List where INT=1 is Index=0
}
return t;
}
HashSet<int> FillDefaultList(int maxInt) //Fills a List with [1,max] integers
{
HashSet<int> l = new HashSet<int>();
for (int i = 1; i <= max; i++)
{
l.Add(i);
}
return l;
}
List<int> FillSquares(int max) //Creates a list of the squares of all values from [2,max] below the max + max-1 sum
{
List<int> l = new List<int>();
int maxSum = max + (max - 1);
for (int i = 2; i < max; i++)
{
if ((i * i) <= maxSum)
{
l.Add(i * i);
}
}
return l;
}
List<int> RemoveElement(List<int> list, int number)
{
List<int> l = new List<int>();
for (int i = 0; i < list.Count; i++)
{
if (list[i] != number)
{
l.Add(list[i]);
}
}
return l;
}
HashSet<int> FillWorklist(HashSet<int> defList, int cNum)
{
HashSet<int> l = defList;
l.Remove(cNum);
return l;
}
void ShowSucces()
{
Console.Write("Found a path with starting number: " + branch[0]);
if (squares.Contains(branch[0] + branch[branch.Count - 1])) //Is it cyclic?
{
cyclic++;
Console.WriteLine(" - This branch is cyclic! ");
}
else
{
Console.WriteLine();
}
for (int i = 0; i < max - 1; i++)
{
Console.Write(branch[i] + ",");
}
Console.WriteLine(branch[max - 1]);
}
}
}