В качестве упражнения мне нужно складывать значения массива, пока не будет достигнуто определенное значение, показывая все возможные комбинации.

Пример: значение = 4, массив = {1, 2, 3}

Возможные комбинации: 1 + 1 + 1 + 1, 1 + 1 + 2, 1 + 2 + 1, 1 + 3, 2 + 1 + 1, 2 + 2, 3 + 1.

Однако мой код, похоже, не работает:

public static void main(String[] args) {

    double[] array = { 1., 2., 3. };
    double value = 4.;
    for (int i = 0; i < array.length; i++) {
        addNumber(array, value, i);
    }
}

public static void addNumber(double[] array, double value, int index) {

    double startNumber = array[index];
    double checkSum = 0;

    for (int i = 0; i < array.length; i++) {
        checkSum += array[i] + startNumber;
        if (checkSum == value){
        System.out.println(startNumber + " + " + array[i] + " = " + checkSum);
        } else if (checkSum < value){
            moreNumbers(array, value, checkSum);
        }
        checkSum = 0;
    }
}

public static void moreNumbers (double[] array, double value, double current){

    if (current == value){
        System.out.println(current);
    } else if (current < value) {

        for (int i = 0; i < array.length; i++){             
            current += array[i];
            System.out.println("+ " + array[i] + " = " + current);              
        }
        moreNumbers(array, value, current);
    }       
}

Выход:

+ 1.0 = 3.0
+ 2.0 = 5.0
+ 3.0 = 8.0
+ 1.0 = 4.0
+ 2.0 = 6.0
+ 3.0 = 9.0
1.0 + 3.0 = 4.0
+ 1.0 = 4.0
+ 2.0 = 6.0
+ 3.0 = 9.0
2.0 + 2.0 = 4.0
3.0 + 1.0 = 4.0

Я считаю, что у меня проблемы с поиском правильного алгоритма, так как я получаю только некоторые комбинации, но не все.

И у меня есть вопрос: я ищу алгоритм, который поможет мне понять это упражнение и его логику, а не окончательный код.

РЕДАКТИРОВАТЬ: В дальнейшем развитии этого упражнения я должен использовать такие числа, как 0,5 или 0,2, но числа всегда положительные, так что это еще одна проблема, на которую я надеюсь найти ответы.

2
abrjak01 22 Фев 2016 в 12:49

3 ответа

Лучший ответ

Общий подход:

  1. Выберите одно из доступных чисел и посмотрите, соответствует ли оно целевому.
  2. Если нет, вы можете выбрать любой другой номер из доступных и добавить его к ранее выбранному номеру; еще раз проверьте, достигли ли вы цели.
  3. Продолжайте, пока не достигнете (или не превысите) целевую сумму.

Это можно реализовать так:

public static void startRecursion(int target, int[] numbers) {
  int min = numbers[0];
  for (int i = 1; i < numbers.length; ++i) {
    min = Math.min(min, numbers[i]);
  }
  // We need to choose at most ceil(target / min) numbers.
  int maxPicks = (target + min - 1) / min;
  recurse(new int[maxPicks], 0, 0, target, numbers);
}

private static void recurse(
    int[] picked, int numPicked, int sumOfPicked,
    int target, int[] numbers) {
  if (sumOfPicked == target) {
    // We reached the target! Print out the numbers we chose to get here.
    for (int i = 0; i < numPicked; ++i) {
      if (i != 0) System.out.print(" + ");
      System.out.print(picked[i]);
    }
    System.out.println(" = " + target);
  } else if (sumOfPicked < target) {
    // We haven't reached the target yet.
    // Go through each of the numbers that you can choose from numbers
    // in turn, increasing the sum by this amount.
    for (int i = 0; i < numbers.length; ++i) {
      picked[numPicked] = numbers[i];
      recurse(
          picked, numPicked + 1, sumOfPicked + numbers[i],
          target, numbers);
    }
  } else {
    // We have overshot the target. Since no numbers are negative,
    // we can't get back to the target again.
  }
}
1
Andy Turner 22 Фев 2016 в 12:17

Вот одно из решений, основанное на Комбинированной Технике.

Алгоритм:

  1. Вычислите всю комбинацию (с повторением) от длины {input} до 1.
  2. Выведите только те комбинации, которые удовлетворяют условию суммы.

Например, если введено 4, то некоторые из различных комбинаций длины = 4 будут следующими:

1 1 1 1
1 1 1 2
.
.
2 1 2 3
.
.
3 3 3 3

Теперь мы напечатаем только 1 1 1 1, так как он суммирует входные данные, то есть 4 и удовлетворяет условию.

Точно так же для length = 3 некоторые из различных комбинаций будут следующими:

1 1 1
1 1 2
.
.
1 2 1
.
.
3 3 3

Теперь мы напечатаем только 1 1 2, 1 2 1 и 2 1 1, поскольку все они удовлетворяют условию суммы.

Вот краткое описание того, как вычисляются различные комбинации:

  1. Комбинированная функция проверяет последний элемент массива и увеличивает его, если он не равен MAX, и выполняет ветвление функции.
  2. Если последний элемент равен max, мы просматриваем массив в поисках следующего числа, отличного от MAX.
  3. Если вышеупомянутое сканирование завершается неудачно, поскольку массив исчерпан, мы возвращаем поток в нашу основную функцию, но, если сканирование возвращает позицию, которая не является максимальной, мы увеличиваем значение в этой позиции на 1 и сбрасываем все значения после этой позиции на МИН. Мы снова разветвляем функцию.

(Он вычисляет все комбинации для заданной длины с использованием рекурсии)

Фрагмент кода:

class Combinations
{
    /* Constants Array (Sorted) */
    private static final double[] ARR_CNST = {0.1,0.2,0.3};
    /* Size of Constant Array */
    private static final int SIZE = 3;

    public static void main (String[] args)
    {
        /* Input Number */
        double input = 0.4;
        /* Start Permutations {Length --> 1} */
        for(int i = (int) (input/ARR_CNST[0]); i > 0; i--) {
            double[] storage = new double[i];
            /* Fill Array With Least Element */
            Arrays.fill(storage,ARR_CNST[0]);
            /* Check Sum Condition */
            if(check(storage,input)) {
                /* Print */
                print(storage);
            }
            /* Calculate Rest of the Combinations */
            calc(storage, input);
        }
    }

    private static void calc(double[] arr, double input) {
        /* Increment Last Element if not MAX */
        if(arr[arr.length - 1] < ARR_CNST[SIZE - 1]) {
            int k = 0;
            while(k < SIZE && arr[arr.length - 1] != ARR_CNST[k++]);
            arr[arr.length - 1] = ARR_CNST[k];
            if(check(arr,input)) {
                print(arr);
            }
            calc(arr, input);
        }

        /* Increment & Reset */
        int i = arr.length - 1;
        while(i >= 0 && arr[i] >= ARR_CNST[SIZE - 1])
            i--;

        if(i >= 0) {
            int k = 0;
            while(k < SIZE && arr[i] != ARR_CNST[k++]);
            arr[i] = ARR_CNST[k];

            for(int x = i + 1; x < arr.length; x++) {
                arr[x] = ARR_CNST[0];
            }

            if(check(arr,input)) {
                print(arr);
            }
            calc(arr, input);
        }

        /* Return */
        return;
    }

    /* Check Sum Condition */
    private static boolean check(double[] arr, double input) {
        double sum = 0;
        for(int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        if(sum == input) {
            return true;
        }
        return false;
    }

    /* Print Array Values */
    private static void print(double[] arr) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < arr.length; i++) {
            sb.append(arr[i] + " + ");
        }
        System.out.println(sb.substring(0,sb.length() - 3));
    }
}

Выход:

0.1 + 0.1 + 0.1 + 0.1
0.1 + 0.1 + 0.2
0.1 + 0.2 + 0.1
0.2 + 0.1 + 0.1
0.1 + 0.3
0.2 + 0.2
0.3 + 0.1
2
user2004685 22 Фев 2016 в 13:00

Кажется, это легко решается с помощью рекурсии, например:

Получение решения для 4 и {1,2,3} (записанного ниже как решение (4, {1,2,3}) похоже на получение решения для

  • «1 +» + решение (3, {1, 2, 3})
  • «2 +» + решение (2, {1, 2, 3})
  • «3 +» + решение (1, {1, 2, 3})

На каждом шаге вы уменьшаете число (если, конечно, в списке доступных номеров нет 0), поэтому вы уверены, что рекурсия завершится.

У вас может быть два исхода:

  • нет возможности (например, вам нужно произвести 1, но в списке доступных номеров нет 1)
  • 1 или несколько потенциальных решений

Есть еще одна вещь, на которую следует обратить внимание: равенство с плавающей запятой . == не будет работать каждый раз.

Код хотел бы:

public static void main(String[] args) {
    ArrayList<String> solutions = new ArrayList<String>();
    solve("", 1.0d, new Double[] {0.2d, 0.50d}, solutions);
    System.out.println(solutions);
    // [0.2 + 0.2 + 0.2 + 0.2 + 0.2, 0.5 + 0.5]
    solutions.clear();
    solve("", 4d, new Double[] {1d, 2d, 3d}, solutions);
    System.out.println(solutions);
    // [1.0 + 1.0 + 1.0 + 1.0, 1.0 + 1.0 + 2.0, 1.0 + 2.0 + 1.0, 1.0 + 3.0, 2.0 + 1.0 + 1.0, 2.0 + 2.0, 3.0 + 1.0]
}

public static void solve(String subSolution, Double remaining, Double[] authorizedNumbers, List<String> solutions) {
    if (doubleEquals(remaining, 0d)) {
        solutions.add(subSolution);
    } else {
        for(Double authorizedNumber : authorizedNumbers) {
            if (doubleEquals(authorizedNumber, remaining)) {
                solutions.add(subSolution + authorizedNumber);
            } else if (authorizedNumber < remaining) {
                solve(subSolution + authorizedNumber + " + ", remaining - authorizedNumber, authorizedNumbers, solutions);
            }
        }
    }
}

public static boolean doubleEquals(double d1, double d2) {
    return Math.abs(d1 - d2) < 0.000000001d;
}
2
Thierry 22 Фев 2016 в 12:33