使用递归找到最大乘积

huangapple go评论73阅读模式
英文:

Find maximum product using recursion

问题

以下是您要求的翻译内容:

有一个问题我看到了,我想知道是否可能使用递归来解决它。问题如下:

编写一个算法,当给定一个输入数组时,找到这些输入中的最大乘积。例如:

输入:[1, 2, 3]
输出:6 (1*2*3)
输入:[-1, 1, 2, 3]
输出:6 (1*2*3)
输入:[-2, -1, 1, 2, 3]
输出:12 (-2*-1*1*2*3)

我试图找到一种使用递归来解决的方法,但我尝试的算法不起作用。我尝试的算法是用Java编写的,如下所示:

Integer[] array;
public int maximumProduct(int[] nums) {
   array = new Integer[nums.length];
   return multiply(nums, 0);
}

public int multiply(int[] nums, int i) {
    if (array[i] != null) {
        return array[i];
    }
    if (i == (nums.length - 1)) {
        return nums[i];
    }
    int returnval = Math.max(nums[i] * multiply(nums, i + 1), multiply(nums, i + 1));
    array[i] = returnval;
    return returnval;
}

这个算法的问题是,如果负数的数量是偶数,它无法很好地工作。例如,如果nums[0] = -2,nums[1] = -1,nums[2] = 1,那么multiply(nums, 1)将始终返回1而不是-1,因此它将始终认为1比1 * -2大,在multiply(nums, 0)中。然而,我不确定如何解决这个问题。是否有任何方法可以使用递归或动态规划来解决这个问题?

英文:

There's a question I saw and I'm wondering if it's possible to solve it using recursion. It goes as follow:

Write an algorithm that, when given an array of input, finds the maximum product from those inputs. For example:

Input: [1, 2, 3]
Output: 6 (1*2*3)
Input: [-1, 1, 2, 3]
Output: 6 (1*2*3)
Input: [-2, -1, 1, 2, 3]
Output: 12 (-2*-1*1*2*3)

I'm trying to find a way of using recursion to solve it, but the algorithm I tried doesn't work. My algorithm, written in Java is as follow

Integer[] array;
public int maximumProduct(int[] nums) {
   array=new Integer[nums.length];
   return multiply(nums, 0);
}

public int multiply(int[] nums, int i){
    if (array[i]!=null){
        return array[i];
    }
    if (i==(nums.length-1)){
        return nums[i];
    }
    int returnval=Math.max(nums[i]*multiply(nums, i+1), multiply(nums, i+1));
    array[i]=returnval;
    return returnval;
    
}

The problem with this algorithm is that it doesn't work well if there's an even number of negative numbers. For example, if nums[0]=-2, nums[1]=-1 and nums[2]=1, then multiply(nums, 1) will always return 1 instead of -1, and thus it will always see 1 as bigger than 1*-2 at multiply(nums, 0). I'm not sure how to solve this problem, however. Is there any way of solving this using recursion or dynamic programming?

答案1

得分: 1

整数的最大乘积是多少?

为了获得最大的乘积,您将希望将所有正整数与最大负整数的乘积相乘,负整数的数量包含在乘积中,以获得最终的正结果。

在单次遍历的算法中

我将分别处理输入中的正整数和负整数。您将希望保持正整数的运行乘积,负整数的运行乘积以及迄今为止找到的最大负整数(即绝对值最小的负整数)。

让我们忽略最终答案小于等于 0 的边界情况。这可以很容易地处理。

//初始化
int [] nums // 输入数组
int posProduct = 1;
int negProduct = 1;
int smallestNeg = 1;

//遍历输入数组
for (int i : nums) {
  if ( i == 0 ) {
    // 忽略
  } else if ( i < 0 ) {
    if (smallestNeg == 1) {
      smallestNeg = i;
    } else if ( i > smallestNeg ) {
      negProduct *= smallestNeg; // 将旧的最小负整数整合到运行乘积中
      smallestNeg = i;           // i 是新的最小值
    } else {
      negProduct *= i;
    }
  } else {
    // i 严格为正
    posProduct *= i;
  }
}

//计算结果
int result = posProduct;
if ( negProduct < 0 ) {
  // 负整数的运行乘积为负数
  // 我们使用最小负整数将其转换回正乘积
  result *= smallestNeg;
  result *= negProduct;
} else {
  result *= negProduct;
}

编辑:在递归遍历中

我个人认为以递归方式编写数组遍历比较繁琐,但确实可行。出于练习的美感和实际回答提问者的问题,这里是我会如何做的示例。

public class RecursiveSolver {
  public static int findMaxProduct(int[] nums) {
    return recursiveArrayTraversal(1, 1, 1, nums, 0); 
  }

  private static int recursiveArrayTraversal(int posProduct, int negProduct, 
      int smallestNeg, int[] nums, int index) {
    if (index == nums.length) {
      // 递归结束,我们遍历了整个数组
      posProduct *= negProduct;
      if (posProduct < 0) {
        posProduct *= smallestNeg;
      }
      return posProduct;
    }

    // 处理数组的 "index" 元素
    int i = nums[index];
    if (i == 0) {
      // 忽略
    } else if (i < 0) {
      if (smallestNeg == 1) {
        smallestNeg = i;
      } else if (i > smallestNeg) {
        negProduct *= smallestNeg; 
        smallestNeg = i;
      } else {
        negProduct *= i;
      }
    } else {
      // i 严格为正
      posProduct *= i;
    }
    
    // 递归调用!
    // 注意 index+1 用作 index 参数,它携带了数组遍历的进度
    return recursiveArrayTraversal(posProduct, negProduct, 
      smallestNeg, nums, index+1);
  }
}
英文:

What is the maximum product of integers?

To obtain the maximum sum, you will want to multiply all the positive integers with the product of the largest negative integers, with the number of negative integers included in the product being even to obtain a positive final result.

In an algorithm for a single traversal

I am going to treat the positive integers and the negative integers in the input separately. You will want to keep a running product of positive integers, a running product of negative integers and the largest negative integer (ie. the negative integer with the smallest absolute value) found so far.
Let us ignore the edge cases where the final answer is <= 0. That can be handled easily.

//Initialization
int [] nums // Input
int posProduct = 1;
int negProduct = 1;
int smallestNeg = 1;

//Input Traversal
for (int i : nums) {
  if ( i == 0 ) {
    // ignore
  } else if ( i &lt; 0 ) {
    if (smallestNeg == 1) {
      smallestNeg = i;
    } else if ( i &gt; smallestNeg ) {
      negProduct *= smallestNeg; //Integrate the old smallest into the running product
      smallestNeg = i;           // i is the new smallest
    } else {
      negProduct *= i;
    }
  } else {
    // i is strictly positive
    posProduct *= i;
  }
}

//Result Computation
int result = posProduct;
if ( negProduct &lt; 0 ) {
  // The running product of negative number numbers is negative
  // We use the smallestNeg to turn it back up to a positive product
  result *= smallestNeg;
  result *= negProduct;
} else {
  result *= negProduct
}

edit: In a recursive traversal

I personally find that writing the array traversal in a recursive manner to be clumsy but it can be done.
For the beauty of the exercise and to actually answer the question of the OP, here is how I would do it.

public class RecursiveSolver {
  public static int findMaxProduct (int [] nums) {
    return recursiveArrayTraversal(1, 1, 1, nums, 0); 
  }

  private static int recursiveArrayTraversal(int posProduct, int negProduct, 
      int smallestNeg, int [] nums, int index) {
    if (index == nums.length) {
      // End of the recursion, we traversed the whole array
      posProduct *= negProduct;
      if (posProduct &lt; 0) {
        posProduct *= smallestNeg;
      }
      return posProduct;
    }

    // Processing the &quot;index&quot; element of the array
    int i = nums[index];
    if ( i == 0 ) {
      // ignore
    } else if ( i &lt; 0 ) {
      if (smallestNeg == 1) {
        smallestNeg = i;
      } else if ( i &gt; smallestNeg ) {
        negProduct *= smallestNeg; 
        smallestNeg = i;
      } else {
        negProduct *= i;
      }
    } else {
      // i is strictly positive
      posProduct *= i;
    }
    
    //Recursive call here! 
    //Notice the index+1 for the index parameter which carries the progress 
    //in the array traversal
    return recursiveArrayTraversal(posProduct, negProduct, 
      smallestNeg, nums, index+1);
  }
}

答案2

得分: 1

如果数组中只有一个非零元素,而且恰好是负数,那么答案要么是0(如果输入中有0),要么如果数组只包含这个单独的负数元素,答案就是这个元素本身。

在所有其他情况下,最终答案将是正数。

首先,我们进行线性扫描以找到负整数的数量。如果这个数字是偶数,那么答案就是所有非零元素的乘积。如果负数元素的数量是奇数,我们需要从答案中去掉一个负数元素,以使答案为正数。因为我们想要最大可能的答案,所以我们希望去掉的数字的绝对值尽可能小。因此,在所有负数中,找到具有最小绝对值的负数,并找到其余非零元素的乘积,这将是答案。

所有这些只需要对数组进行两次线性扫描,因此在O(n)时间内运行。

英文:

If there is only one non-zero element in the array, and it happens to be a negative number, then then answer is either 0, if there is a 0 present in the input, or if the array contains only that single negative element, the answer is that element itself.

In all other cases, the final answer is going to be positive.

We first make a linear scan to find the number of negative integers. If this number is even, then the answer is the product of all the non-zero elements. If there are an odd number of negative elements, we need to leave out one negative element from the answer, so that the answer is positive. As we want the maximum possible answer, the number we want to leave out should have as small an absolute value as possible. So among all the negative numbers, find the one with the minimum absolute value, and find the product of the remaining non-zero elements, which should be the answer.

All this requires only two linear scans of the array, and hence runs in O(n) time.

答案3

得分: 0

Linear版本

List<Integer> vals = new ArrayList<>(List.of(5, 1, -2, 1, 2, 3, -4, -1));

int prod = 0;
int min = 1;
for (int v : vals) {
    if (v == 0) {
        // 忽略零值
        continue;
    }
    if (prod == 0) {
        prod = 1;
    }
    prod *= v;
    // 计算min为列表中的最大负值
    if (v < 0 && min < Math.abs(v)) {
        min = v;
    }
}
if (prod < 0) {
    prod /= min;
}

System.out.println("最大乘积 = " + prod);
}

递归版本

int prod = prod(vals, new int[] {0}, vals.size());
System.out.println("最大乘积 = " + prod);

public static int prod(List<Integer> vals, int[] min, int size) {
    int prod = 0;
    if (vals.size() > 0) {
        int t = vals.get(0);
        if (t < 0 && min[0] < Math.abs(t)) {
            min[0] = t;
        }
        prod = prod(vals.subList(1, vals.size()), min, vals.size());
    }
    if (vals.isEmpty() || vals.get(0) == 0) {
        return prod;
    }

    if (prod == 0) {
        prod = 1;
    }
    prod *= t;

    if (vals.size() == size && prod < 0) {
        prod /= min[0];
    }
    return prod;
}
英文:

Linear version

	    List&lt;Integer&gt; vals = new ArrayList&lt;&gt;(List.of(5,1,-2,1,2,3,-4,-1));
	
		int prod = 0;
		int min = 1;
		for (int v : vals) {
			if (v == 0) {
                // ignore zero values
				continue;
			}
            if (prod == 0) {
                prod = 1;
            }
			prod *= v;
            // compute min to be the largest negative value in the list.
			if (v &lt; 0 &amp;&amp; min &lt; Math.abs(v)) {
				min = v;
			}
		}
		if (prod &lt; 0) {
			prod /= min;
		}
		
		System.out.println(&quot;Maximum product = &quot; + prod);
	}

Recursive version

		int prod = prod(vals, new int[] {0} , vals.size());
		System.out.println(&quot;Maximum product = &quot; + prod);
	
	public static int prod(List&lt;Integer&gt; vals, int[]min, int size) {
	    int prod = 0;
	    if(vals.size() &gt; 0)	{
	    	int t = vals.get(0);
	    	 if (t &lt; 0 &amp;&amp; min[0] &lt; Math.abs(t)) {
			    	min[0] = t;
			 }
			 prod = prod(vals.subList(1,vals.size()), min, vals.size());
		}
		if (vals.isEmpty() || vals.get(0) == 0) {
			return prod;
		}
	  
	    if (prod == 0) {
           prod = 1;
        }
		prod *= t;
		
		if (vals.size() == size &amp;&amp; prod &lt; 0) {
			prod/=min[0];
		}
		return prod;	
	}

</details>



# 答案4
**得分**: 0

以下是翻译好的内容:

这是我的解决方案 - 为了优化和计算运行时留下了空间。这是一个通用的解决方案,可以找到列表中所有整数的**所有组合**的乘积。当然,存在一个O(n)的解决方案,但我也提供这个解决方案。

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

public class MaxProd {
    
    int[] input = {1, 2, 3};
    
    // int[] input = {-2, -1, 1, 2, 3};
    
    public static void main(String[] args) {
        MaxProd m = new MaxProd();
        List<Integer> ll = m.max(0);
        for (int i : ll) {
            System.out.println(i);
        }
        ll.sort((x,y) -> Integer.compare(x, y));
        System.out.println("最大值: " + ll.get(ll.size() - 1));
    }

    private List<Integer> max(int index) {
        if (index < input.length){
            List<Integer> l = new ArrayList<>();
            List<Integer> retList = max(index + 1);
            
            for (int j : retList){
                l.add(input[index] * j);
            }
            l.add(input[index]);
            l.addAll(retList);
            return l;
        }
        else return new ArrayList<>();
    }
}

它会打印出:

6
2
3
1
6
2
3
最大值: 6

如果要求是有限制的(如本例),那么可以在不生成所有组合的情况下得到一个线性解决方案。此外,我在最后进行了排序。注意:你可以轻松地通过一次遍历返回的列表来找到最大乘积,正如其他答案中指定的那样。

英文:

This is my solution - leaving it open for optimization and to figure out the runtime. This is a general purpose solution that finds the products of all the combinations of integers in a list. Of course, there is a O(n) solution but I present this solution as well.

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

public class MaxProd {
    
    int[] input = {1, 2, 3};
    
    // int[] input = {-2, -1, 1, 2, 3};
    
    public static void main(String[] args) {
        MaxProd m = new MaxProd();
        List&lt;Integer&gt; ll =  m.max(0);
        for (int i : ll) {
            System.out.println(i);
        }
        ll.sort((x,y) -&gt; Integer.compare(x, y));
        System.out.println(&quot;The max: &quot; + ll.get(ll.size() -1 ));

    }

    private List&lt;Integer&gt; max(int index) {
        if (index &lt; input.length){
            List&lt;Integer&gt; l = new ArrayList&lt;&gt;();
            List&lt;Integer&gt; retList = max(index + 1);
            
            for (int j : retList){
                l.add(input[index] * j);
            }
            l.add(input[index]);
            l.addAll(retList);
            return l;
        }
        else return new ArrayList&lt;&gt;();

    }
}

it prints:

6
2
3
1
6
2
3
The max: 6

If the requirements are constrained (as in this case) then one can get by without the need for generating all combinations resulting in a linear solution. Also, I'm sorting at the end. Note: you could easily get the result with a single pass on the returned list to find the maximum product as specified in other answers.

答案5

得分: 0

首先,在列表中每次找到一个 0 时,将数组分割成子问题:

 1 -2  4 -1  8  0  4  1  0 -3 -4  0  1  3 -5
|_____________|   |____|   |____|   |_______|
       p1           p2       p3         p4 

然后,对于每个问题 pi,计算其中有多少个负数。

如果 pi 中有偶数个负数(或者根本没有负数),则 pi 的答案是其所有元素的乘积。

如果 pi 中只有一个负数(记为 n),那么答案将是 n 右侧所有元素的乘积与 n 左侧所有元素乘积的最大值。

如果 pi 中有奇数个(大于 1 个)负数,则将最左边负数的索引记为 l,将最右边负数的索引记为 r。假设 pin 个元素,则答案为:

max(
    pi[  0  ] * pi[  1  ] * ... * pi[r - 1],
    pi[l + 1] * pi[l + 2] * ... * pi[  n  ]
)

有了这些信息,很容易为解决这个问题的每个步骤编写递归:一个递归用于在零处分割问题,另一个递归用于计算负数数量,还有一个递归用于找到答案,时间复杂度为 O(n)。

英文:

First, break the array in subproblems always you find a 0 in the list:

 1 -2  4 -1  8  0  4  1  0 -3 -4  0  1  3 -5
|_____________|   |____|   |____|   |_______|
       p1           p2       p3         p4 

Then, for each problem pi, count how many negative numbers are there.

If pi has an even number of negatives (or no negatives at all), the answer of pi is the product of all its elements.

If pi has only 1 negative number (say n), the answer will be the maximum between the product of all the elements in n's right and the product of all elements in n's left.

If pi has an odd number (bigger than only 1) of negative numbers, call the index of the leftmost negative number l and the index of the rightmost negative number r. Supposing pi has n elements, the answer will be:

max(
    pi[  0  ] * pi[  1  ] * ... * pi[r - 1],
    pi[l + 1] * pi[l + 2] * ... * pi[  n  ]
)

Knowing that, it's easy to write a recursion for each step of the solution of this problem: a recursion to divide problems at zeros, another to count negatives and another to find answers, in O(n).

huangapple
  • 本文由 发表于 2020年4月6日 08:07:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/61051125.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定