我最近的一个爱好项目,通过向我自己挑战,以某种方式让我受益,让自己更好地编写代码。这个算法基本上是我试图为我玩的游戏使用一堆简单数学的解算器的一部分。
我有一个七到九个数字的列表,每个数字可以是1-9的任何整数,允许重复。例如:{6,3,8,9,1,3,7}
我想生成第二个数字列表(一个集合,这里没有重复),这是通过将原始数字相加,相减或相乘来获得的所有可能结果。每个数字只能使用一次,除非它多次出现在初始列表中。
所以我希望每个数字本身(简单),每个数字相互添加/ sub'd / mult'd(简单),然后添加,sub'ing和multing的每个可能的组合。 编辑:我忘了提到为了这些目的,操作优先级(加法/减法前的乘法)并不重要。我正在模仿的游戏单独处理每个操作,因此6+1*7
将是49,而不是42。
我已经阅读了一点,我认为树木将成为我解决方案的重要组成部分,但我对如何继续下去感到有些茫然。我已经对树木进行了介绍,但我对他们不太满意,真正了解他们如何帮助我。我将把这个算法放到代码中,很可能是Java或C的版本。
正如我所提到的,这是一个自我改进的项目,所以我不仅需要一个预先编码的解决方案,我确定GitHub上的某人已经完成了这一点。< / p>
答案 0 :(得分:1)
UPD:所以我可以使用以下动态编程:
对于数字S的集合,我们将F(S)称为一组所有可能的数字,可以从问题中描述的S元素推导出来。
对于给定数字集的每个子集S,我们将存储答案:F(S)。
我使用以下算法计算子集的答案:
集合S中的所有数字都在F(S)中。
如果数字x在子集S中,我们应该将F(S - x)添加到F(S),因为我们可以跳过一些数字
遍历S的所有子集.Lets表示当前子集为L,S - L表示为R.因此,对于F(L)中的每个数字x,F(R)中的数字y和(+)中的运算符o , - ,*)我们应该将xoy添加到F(S)。
在代码中,我们将子集表示为位掩码:当且仅当第i位是1时,第i个数是子集的一部分。所以我们有2 ^ 9个子集。让我们按照从1到(1 < - 9)-1的位掩码的顺序计算子集的答案。
代码如下所示:
for (int mask = 1; mask < (1 << n); ++mask){
set<int> result;
for (int i = 0; i < n; ++i) {
if (getBit(mask, i) == 1) {
result.insert(z[mask ^ (1 << i)].begin(), z[mask ^ (1 << i)].end()); //if we do not want to use some number, we erase it from the mask with xor
result.insert(a[i]); // we always can take number itself if we have it in mask
}
}
for (int left_mask = (mask - 1) & mask; left_mask > 0; left_mask = (left_mask - 1) & mask) { //this loop will iterate through all submasks of mask
int right_mask = mask ^ left_mask;
for (int i = 0; i < (int) z[left_mask].size(); ++i)
for (int j = 0; j < (int) z[right_mask].size(); ++j) {
for (int op = 0; op < 3; ++op)
result.insert(applyOp(z[left_mask][i], z[right_mask][j], op));
}
}
z[mask] = vector<int>(result.begin(), result.end());
}
因此,对于8个数字,这个工作约为3秒,对于9个数字约为1分钟,但只有一个可能的输入有9个数字,9个输入有8个数字,因此可以提前计算并存储它们的答案。对于7个数字,这大约300毫秒。
以下答案已过时且错误,因为它不考虑像* b + c * d
这样的情况我认为记忆力强大的人可以快速工作,最多9个号码。 bruteforce算法的状态将是两个整数:当前生成的数字和来自set的未使用数字的位掩码。您只需遍历未使用的数字和可用操作,并将操作应用于当前生成的数字和当前未使用的数字,并更新您的状态。代码如下所示:
vector<int> a; //initial numbers
set<int> result;
set< pair<int, int> > used; //visited bruteforces states
void brute(const pair<int, int>& state) {
if (used.count(state))
return;
used.insert(state);
result.insert(state.first);
int current = state.first;
int mask = state.second;
for (int i = 0; i < a.size(); ++i) {
if (((mask >> i) & 1) == 0) // if i-th bit is zero, then number is used
continue;
for (int op = 0; op < 3; ++op) {
int next = current;
if (op == 0)
next += a[i];
if (op == 1)
next -= a[i];
if (op == 2)
next *= a[i];
brute(make_pair(next, mask ^ (1 << i)));
}
}
}
//...
// to start the process use brute(0, (1 << a.size()) - 1);
我在计算机上运行了全九位数。它的工作时间约为2.5秒,并产生了192571个数字。对于6位数,它几乎立即工作。 8位数 - 约300毫秒。
答案 1 :(得分:0)
这是一种继续进行的方式(这是计算器的方式): -
- 将数字集转换为中缀表达式
- 执行中缀到后缀表达式转换
- 使用堆栈解决后缀
- 将结果数存储到哈希集中以拒绝重复项。
醇>
答案 2 :(得分:-2)
很好的问题和有趣的解决方案。继承人我的解决方案。它是一个递归方法:(它包括在negatif中的你的号码,这意味着如果你的列表中有3个,数字-3将在解决方案中)
import java.io.IOException;
import java.util.ArrayList;
public class test {
static ArrayList<Integer> myresult = new ArrayList<Integer>();
public static void main(String[] args) throws IOException
{
int[] a = new int[]{3,5,7};
for(int i = 0 ; i < a.length ; ++i)
calculate(a, 0 , 0);
for(int j = 0 ; j < myresult.size(); ++j)
System.out.println("number " + j + " = " + myresult.get(j));
}
public static void calculate(int[] a , int initial , int leftvalues)
{
if(leftvalues == (a.length-1) )
{
if(!myresult.contains(a[a.length-1]))
myresult.add(a[a.length-1]);
return ;
}
else
{
int add;
int sub;
for(int i = leftvalues ; i < a.length ; ++i)
{
add = initial + a[i];
if(!myresult.contains(add))
myresult.add(add);
calculate(a , add,leftvalues+1);
sub = initial - a[i];
if(!myresult.contains(sub))
myresult.add(sub);
calculate(a , sub,leftvalues+1);
}
}
}
}
希望这很适合你:)