Freeciv21
Develop your civilization from humble roots to a global empire
distribute.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
3  __ __ part of Freeciv21. Freeciv21 is free software: you can
4 / \\..// \ redistribute it and/or modify it under the terms of the GNU
5  ( oo ) General Public License as published by the Free Software
6  \__/ Foundation, either version 3 of the License, or (at your
7  option) any later version. You should have received
8  a copy of the GNU General Public License along with Freeciv21. If not,
9  see https://www.gnu.org/licenses/.
10  */
11 
12 #include <vector>
13 // utility
14 #include "log.h" // fc_assert
15 
16 #include "distribute.h"
17 
29 void distribute(int number, int groups, int *ratios, int *result)
30 {
31  int i, sum = 0, max_count, max;
32  std::vector<int> rest, max_groups;
33  rest.resize(groups);
34  max_groups.resize(groups);
35 #ifdef FREECIV_DEBUG
36  const int original_number = number;
37 #endif
38 
39  /*
40  * Distribution of a number of items into a number of groups with a given
41  * ratio. This follows a modified Hare/Niemeyer algorithm (also known
42  * as "Hamilton's Method"):
43  *
44  * 1) distribute the whole-numbered part of the targets
45  * 2) sort the remaining fractions (called rest[])
46  * 3) divide the remaining source among the targets starting with the
47  * biggest fraction. (If two targets have the same fraction the
48  * target with the smaller whole-numbered value is chosen. If two
49  * values are still equal it is the _first_ group which will be given
50  * the item.)
51  */
52 
53  for (i = 0; i < groups; i++) {
54  fc_assert(ratios[i] >= 0);
55  sum += ratios[i];
56  }
57 
58  // 1. Distribute the whole-numbered part of the targets.
59  for (i = 0; i < groups; i++) {
60  result[i] = number * ratios[i] / sum;
61  }
62 
63  // 2a. Determine the remaining fractions.
64  for (i = 0; i < groups; i++) {
65  rest[i] = number * ratios[i] - result[i] * sum;
66  }
67 
68  // 2b. Find how much source is left to be distributed.
69  for (i = 0; i < groups; i++) {
70  number -= result[i];
71  }
72 
73  while (number > 0) {
74  max = max_count = 0;
75 
76  // Find the largest remaining fraction(s).
77  for (i = 0; i < groups; i++) {
78  if (rest[i] > max) {
79  max_count = 1;
80  max_groups[0] = i;
81  max = rest[i];
82  } else if (rest[i] == max) {
83  max_groups[max_count] = i;
84  max_count++;
85  }
86  }
87 
88  if (max_count == 1) {
89  // Give an extra source to the target with largest remainder.
90  result[max_groups[0]]++;
91  rest[max_groups[0]] = 0;
92  number--;
93  } else {
94  int min = result[max_groups[0]], which_min = max_groups[0];
95 
96  /* Give an extra source to the target with largest remainder and
97  * smallest whole number. */
98  fc_assert(max_count > 1);
99  for (i = 1; i < max_count; i++) {
100  if (result[max_groups[i]] < min) {
101  min = result[max_groups[i]];
102  which_min = max_groups[i];
103  }
104  }
105  result[which_min]++;
106  rest[which_min] = 0;
107  number--;
108  }
109  }
110 
111 #ifdef FREECIV_DEBUG
112  number = original_number;
113  for (i = 0; i < groups; i++) {
114  fc_assert(result[i] >= 0);
115  number -= result[i];
116  }
117  fc_assert(number == 0);
118 #endif // FREECIV_DEBUG
119 }
void distribute(int number, int groups, int *ratios, int *result)
Distribute "number" elements into "groups" groups with ratios given by the elements in "ratios".
Definition: distribute.cpp:29
#define fc_assert(condition)
Definition: log.h:89