简体   繁体   中英

find a min value by iterating the array and dividing each element by a number starting with 1

I have a below method which iterates a distance array and divide each element by a number starting with 1 and get the sum. If sum is greater than value points which is passed to the method then start again in the for loop and divide by 2 and keep going until you find a sum which is less than value points .

Below code works but is there any way to write this better?

  public static int findMin(List<Integer> distance, int points) {
    int sum = 0;
    int c = 1;
    while (true) {
      for (Integer dist : distance) {
        sum = (int) (sum + Math.ceil(dist / c));
      }
      if (sum <= points) {
        break;
      }
      c++;
      sum = 0;
    }
    return c;
  }

If there is no specific reason to do Math.ceil to each ratio rather than to the final sum, you can just get the sum of all elements first and then find the value of c

sum / c <= points

sum / points <= c

if 0 < (sum / points) < 1, c = 1

else c = Math.ceil(sum / points)

public static int findMin(List<Integer> distance, int points) {
    AtomicInteger c = new AtomicInteger(1);
    while (distance.stream().mapToInt(d -> d / c.get()).sum() > points) c.incrementAndGet();
    return c.get();
}

Correct me if I'm wrong but assuming the set of distances is [1, 2, 3] right? Then you start with 1/1 + 2/1 + 3/1 which (let's leave them as fractions here) equals 6/1 , since they all have the same "denominator" here, it doesn't change. So that means that the first iteration, dividing by one, is literally the sum of the values. (1 + 2 + 3) / 1 divided by one. And anything divided by 1 is itself. So it's just the sum.

Now. On the second pass, if I assume correctly, 1/2 + 2/2 + 3/2 -- again leaving them as fractions -- (1 + 2 + 3) / 2 = 6/2 . By now you should see a pattern, right? First pass was 6/1 second is 6/2 next will be 6/3 ...

So how about:

public static int findMin(List<Integer> distance, int points) {
    int sum = 0;
    for (Integer i : distance) {
        sum += i;
    }

    int min = 1;
    while (sum / min > points) {
        min += 1;
    }

    return min;
}

Perhaps a solution like this would work?

edit So as it turns out this solution is assuming (at least partially) some mathematical accuracy, however it appears that the division per-element is required to be integer division which skews some of the results if we approach it strictly mathematically. So while not being a direct answer to the problem I feel like it's correct enough to leave here as a solution.

I think,we can do two things to improve the performance,but the method is not the best and it depend on the number of your list:

  • reduce the times of iterate
  • use greedy algorithm priority reduction the max value. In order to do this we first need to sort the list it cost may cost O(nlog(n)) time.

code like this

public static int findMin(List<Integer> distance, int points) {
    int sum = 0;
    int c = 1;
    // sort the list for implement greedy algorithm
    Collections.sort(distance, Comparator.reverseOrder());
    while (true) {
        for (Integer dist : distance) {
            sum += dist / c;
            // reduce the times of iterate
            if (sum <= points) {
                return c;
            }
        }
        c++;
        sum = 0;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM