Java 迭代最大化算法
这是我目前试图解决的问题Java 迭代最大化算法,java,c++,algorithm,Java,C++,Algorithm,这是我目前试图解决的问题 有一个称为T的最大值。然后有两个子值a和B,它们是1这个问题可以看作是一个有T+1节点的有向图。假设我们有T+1节点从0到T,并且我们有一条从节点x到节点y的边,如果: x+A=y x+B=y x/2=y 因此,为了回答这个问题,我们需要在图中进行搜索,声明点是node0 我们可以采取一种或两种方法来解决这个问题 更新:由于我们只能进行一次除法,因此我们必须向图形中添加另一个状态,即isDivided。然而,解决这一问题的方法没有改变 我将用BFS实现演示该解决方案
有一个称为T的最大值。然后有两个子值a和B,它们是1这个问题可以看作是一个有
T+1
节点的有向图。假设我们有T+1
节点从0到T
,并且我们有一条从节点x
到节点y
的边,如果:
- x+A=y
- x+B=y
- x/2=y
0
我们可以采取一种或两种方法来解决这个问题
更新:由于我们只能进行一次除法,因此我们必须向图形中添加另一个状态,即isDivided
。然而,解决这一问题的方法没有改变
我将用BFS实现演示该解决方案,DFS非常类似
class State{
int node, isDivided;
}
boolean[][]visited = new boolean[2][T + 1];
Queue<State> q = new LinkedList();
q.add(new State(0, 0));//Start at node 0, and haven't use division
visited[0][0] = true;
int result = 0;
while(!q.isEmpty()){
State state = q.deque();
result = max(state.node, result);
if(state.node + A <= T && !visited[state.isDivided][state.node + A]){
q.add(new State(node + A , state.isDivided));
visited[state.isDivided][node + A] = true;
}
if(node + B <= T && !visited[state.isDivided][node + B]){
q.add(new State(node + B, state.isDivided));
visited[state.isDivided][node + B] = true;
}
if(state.isDivided == 0 && !visited[state.isDivided][node/2]){
q.add(new State(node/2, 1));
visited[state.isDivided][node/2] = true;
}
}
return result;
类状态{
int节点,isDivided;
}
布尔[][]已访问=新布尔[2][T+1];
队列q=新的LinkedList();
q、 添加(新状态(0,0))//从节点0开始,尚未使用除法
已访问[0][0]=真;
int结果=0;
而(!q.isEmpty()){
状态=q.deque();
结果=最大值(state.node,result);
如果(state.node+A,我们也可以这样描述问题:
f(A , B) = (A * n + B * m) / 2 + (A * x + B * y)
= A * (n * 0.5 + x) + B * (m * 0.5 + y) =
= A * p + B * q
find N: N = f(A , B) and N <= T such that no M: M > N satisfying
the condition exists.
因此,我们知道:
(T / B * 2) mod 1 - (A / B * r) mod 1 is minimal and >= 0 for the optimal solution
T * 2 / A >= r >= 0 are the upper and lower bounds for r
(A / B * r) mod 1 = 0, if r = B / gcd(A , B) * n, where n is an integral number
使用二进制搜索,使用这些约束查找r现在变成了一项简单的任务。可能有更有效的方法,但为此,O(log B)
应该:
Apply a simple binary-search to find the matching value
in the range [0 , min(T * 2 / A , B / gcd(A , B))
对于任何相应的r
,都可以轻松地查找s
:
s = roundDown(T * 2 / B - A * r / B)
例如:
这种方法的优点:我们可以在O(log B)
中找到所有解决方案:
如果找到r的值,则与约束匹配的所有其他值r'如下:r'=r+B/gcd(a,B)*n
a
和B
在这种方法中是可交换的,允许通过使用较小的输入值B
进一步优化
在您的算法中,当变量被二除时,值的舍入应该只会导致一些小问题,这些问题很容易解决。总结一下我所理解的问题设置(在您最多只能被二除一次的限制下):
添加A
和B
任意次数(包括0次)
除以2,四舍五入
添加A
和B
任意次数
目标是获得最大可能的总和,但在算法的任何步骤后,总和不得超过T
这可以在5变量整数程序中巧妙地捕捉到。五个变量是:
a1
:我们在除以2之前添加A
的次数
b1
:我们在除以2之前添加B
的次数
s1
:楼层((A*a1+B*b1)/2)
,第二步后的总金额
a2
:除以2后我们添加A
的次数
b2
:除以2后添加B
的次数
最后的和是s1+A*a2+B*b2
,它被限制为不超过T
;这是我们寻求最大化的结果。所有五个决策变量都必须是非负整数
这个整数程序可以通过整数规划解算器轻松地求解为最优性。例如,下面是如何使用R中的lpSolve
包来求解它:
library(lpSolve)
get.vals <- function(A, B, T) {
sol <- lp(direction = "max",
objective.in = c(0, 0, 1, A, B),
const.mat = rbind(c(A, B, 0, 0, 0), c(0, 0, 1, A, B), c(-A, -B, 2, 0, 0), c(-A, -B, 2, 0, 0)),
const.dir = c("<=", "<=", "<=", ">="),
const.rhs = c(T, T, 0, -1),
all.int = TRUE)$solution
print(paste("Add", A, "a total of", sol[1], "times and add", B, "a total of", sol[2], "times for sum", A*sol[1]+B*sol[2]))
print(paste("Divide by 2, yielding value", sol[3]))
print(paste("Add", A, "a total of", sol[4], "times and add", B, "a total of", sol[5], "times for sum", sol[3]+A*sol[4]+B*sol[5]))
}
我将使用一些数学方法
简历:
您应该能够使用A、B、T计算最大值,而无需迭代(仅获得A/B HCD),对于T,而不是很小
- 如果A或B是奇数,max=T(有保留,我不确定你永远不会超过T:见下文)
- 如果A和B是偶数,则取C作为最高公因数。然后max=四舍五入(T/C*2)*C/2=低于或等于T的C/2的最高倍数
一些解释:
规则为:Ap+Bq(不除以2)
1假设A和B是素数,那么你可以得到你想要的每个整数,在小整数之后。然后max=T
示例:A=11,B=17
2如果A=Cx,B=Cy,x,y素数加在一起(比如10和21),你可以得到每一个C的倍数,那么max=T:round(T/C)*C以下C的最大倍数
示例:A=33,B=51(C=3)
规则是:你可以除以2
3-如果C是偶数(即A和B可以除以2):最大值=T下C/2的倍数:四舍五入(T/C*2)*C/2
示例:A=22,B=34(C=2)
4-否则,您必须找到A,B,round(A/2),round(B/2)的最大dividor(最高公因数),称之为D,max=T以下D的最大倍数:round(T/D)*D
因为A和round(A/2)一起是素数(idem表示B和round(B/2)),然后您可以得到max=T,如案例1-警告:我不确定您是否从未超过T。要检查除了大于或等于1
之外,A
是否有任何限制?限制如下。1a、B、T仅为整数或浮点?如果是整数,如何舍入5/2?将除法舍入。忘记提及That.you最多只能分割一次。这仍然有效吗?@user3188300结果被添加到队列中,因此它不仅被分割一次。假设您将5添加到队列中,那么下一步,我们将(5/2=2)添加到队列中。然后,我们将处理2(已经在队列中),然后添加(2/2=1)到队列。我要问的是,在问题陈述中,你只允许除以2一次。你不能一直除以2。例如,一旦我转到5/2=2,我就不能再除以这个和。@user3188300我明白了,所以实际上,我们需要为解决方案添加另一个维度,即isDivided
。解决这个问题的方法是也没什么不同。等我回来
A = 5
B = 6
T = 8
gcd(A , B) = 1
search-range = [0 , 6)
(T / B * 2) mod 1 = 4 / 6
(A / B * r) mod 1 =
r = 3: 3 / 6 => too small --> decrease r
r = 1: 5 / 6 => too great --> increase r
r = 2: 4 / 6 => optimal solution, r is found
r = 2
s = roundDown(T * 2 / B - A * r / B) = roundDown(3.2 - 1.66) = 1
p = r / 2 = 1 = 1 + 0 = 2 * 0.5 --> n = 1 y = 0 or n = 2 y = 0
q = s / 2 = 0.5 --> n = 0.5 y = 0
8 >= 5 * 1 + 5 * 0.5 * 0 + 0 * 6 + 1 * 0.5 * 6 = 5 + 3
= 5 * 0 + 5 * 0.5 * 2 + 0 * 6 + 1 * 0.5 * 6 = 5 + 3
library(lpSolve)
get.vals <- function(A, B, T) {
sol <- lp(direction = "max",
objective.in = c(0, 0, 1, A, B),
const.mat = rbind(c(A, B, 0, 0, 0), c(0, 0, 1, A, B), c(-A, -B, 2, 0, 0), c(-A, -B, 2, 0, 0)),
const.dir = c("<=", "<=", "<=", ">="),
const.rhs = c(T, T, 0, -1),
all.int = TRUE)$solution
print(paste("Add", A, "a total of", sol[1], "times and add", B, "a total of", sol[2], "times for sum", A*sol[1]+B*sol[2]))
print(paste("Divide by 2, yielding value", sol[3]))
print(paste("Add", A, "a total of", sol[4], "times and add", B, "a total of", sol[5], "times for sum", sol[3]+A*sol[4]+B*sol[5]))
}
get.vals(5, 6, 8)
# [1] "Add 5 a total of 1 times and add 6 a total of 0 times for sum 5"
# [1] "Divide by 2, yielding value 2"
# [1] "Add 5 a total of 0 times and add 6 a total of 1 times for sum 8"
get.vals(17, 46, 5000000)
# [1] "Add 17 a total of 93 times and add 46 a total of 0 times for sum 1581"
# [1] "Divide by 2, yielding value 790"
# [1] "Add 17 a total of 294063 times and add 46 a total of 3 times for sum 4999999"