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

因此,为了回答这个问题,我们需要在图中进行搜索,声明点是node
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:见下文)

    • 如果AB是偶数,则取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"