Related
I'm looking to find an algorithm that successfully generalizes the following problem to n number of sets, but for simplicity assume that there are 4 different sets, each containing 4 elements. Also we can assume that each set always contains an equal number of elements, however there can be any number of elements. So if there are 37 elements in the first set, we can assume there are also 37 elements contained in each of the other sets.
A combination of elements is formed by taking 1 element from the first set and putting it into first place, 1 element from the second set and putting it in the second place, and so on. For example say the first set contains {A0,A1,A2,A3}, the second set contains {B0,B1,B2,B3}, third is {C0,C1,C2,C3} and fourth is {D0,D1,D2,D3}. One possible combination would be [A0, B2, C1, D3].
The goal is to find the path that maximizes the distance when cycling through all the possible combinations, avoiding repetition as much as possible. And avoiding repetition applies to contiguous groups as well as individual columns. For example:
Individual columns
[A0, B0, C0, D0]
[A1, B1, C1, D1]
[A2, B0, C2, D2]
This is incorrect because B0 is repeated sooner than it had to be.
Contiguous groups
[A0, B0, C0, D0]
[A1, B1, C1, D1]
[A2, B2, C2, D2]
[A3, B3, C3, D3]
[A0, B0, C1, D2]
This is incorrect because the contiguous pair (A0, B0) was repeated sooner than it had to be. However if the last one was instead [A0, B1, C0, D1] then this would be alright.
When cycling through all possible combinations the contiguous groups will have to be repeated, but the goal is to maximize the distance between them. So for example if (A0, B0) is used, then ideally all the other first pairs would be used before it's used again.
I was able to find a solution for when there are 3 sets, but I'm having trouble generalizing it to n sets and even solving for 4 sets. Any ideas?
Can you post your solution for three sets?
Sure, first I wrote down all possible combinations. Then I made three 3x3 matrices of entries by grouping the entries where the non-contiguous (first and third) elements were repeated:
(A0,B0,C0)1, (A1,B0,C1)4, (A2,B0,C2)7 (A0,B0,C1)13, (A1,B0,C2)16, (A2,B0,C0)10 (A0,B0,C2)25, (A1,B0,C0)19, (A2,B0,C1)22
(A0,B1,C0)8, (A1,B1,C1)2, (A2,B1,C2)5 (A0,B1,C1)11, (A1,B1,C2)14, (A2,B1,C0)17 (A0,B1,C2)23, (A1,B1,C0)26, (A2,B1,C1)20
(A0,B2,C0)6, (A1,B2,C1)9, (A2,B2,C2)3 (A0,B2,C1)18, (A1,B2,C2)12, (A2,B2,C0)15 (A0,B2,C2)21, (A1,B2,C0)24, (A2,B2,C1)27
Then I realized if I traversed in a diagonal pattern (order indicated by the superscript index) that it would obey the rules. I then wrote the following code to take advantage of this visual pattern:
#Test
public void run() {
List<String> A = new ArrayList<String>();
A.add("0");
A.add("1");
A.add("2");
List<String> B = new ArrayList<String>();
B.add("0");
B.add("1");
B.add("2");
List<String> C = new ArrayList<String>();
C.add("0");
C.add("1");
C.add("2");
int numElements = A.size();
List<String> output = new ArrayList<String>();
int offset = 0;
int nextOffset = 0;
for (int i = 0; i < A.size()*B.size()*C.size(); i++) {
int j = i % numElements;
int k = i / numElements;
if (j == 0 && k%numElements == numElements-1) {
nextOffset = (j+k+offset) % numElements;
}
if (j == 0 && k%numElements == 0) {
offset = nextOffset;
}
String first = A.get((j+k+offset) % numElements);
String second = B.get(j);
String third = C.get((j+k) % numElements);
System.out.println(first + " " + second + " " + third);
output.add(first + second + third);
}
}
However I just realized that this isn't ideal either, since it looks like the pair (A0,B1) is repeated too soon, at indices 8 and 11 :( However I think maybe this is unavoidable, when crossing over from one group to another?.. This is a difficult problem! Harder than it looks
If you can think about and revise your actual requirements
Okay so I decided to remove the restriction of traversing through all possible combinations, and instead reduce the yield a little bit to improve the quality of the results.
The whole point of this is to take elements belonging to a particular set and combine them to form a combination of elements that appear unique. So if I start out with 3 combinations and there are 3 sets, I can break each combination into 3 elements and place the elements into their respective sets. I can then use the algorithm to mix and match the elements and produce 27 seemingly unique combinations -- of course they're formed from derivative elements so they only appear unique as long as you don't look too closely!
So the 3 combinations formed by hand can be turned into 33 combinations, saving a lot of time and energy. Of course this scales up pretty nicely too, if I form 10 combinations by hand then the algorithm can generate 1000 combinations. I probably don't need quite that many combinations anyways, so I can sacrifice some entries to better avoid repetition. In particular with 3 sets I noticed that while my solution was decent, there was some bunching that occurred every numElements2 entries. Here is an example of 3 sets of 5 elements, with an obvious repetition after 25 combinations:
19) A1 B3 C1
20) A2 B4 C2
21) A4 B0 C4 <--
22) A0 B1 C0
23) A1 B2 C1
24) A2 B3 C2
25) A3 B4 C3
26) A0 B0 C4 <--
27) A1 B1 C0
28) A2 B2 C1
29) A3 B3 C2
30) A4 B4 C3
31) A1 B0 C0
32) A2 B1 C1
To fix this we can introduce the following statement and get rid of this bad block:
if (k % numElements == 0) continue;
However this only works when numElements > numSets, otherwise the Individual Columns rule will be broken. (In case you were wondering I also switched the ordering of the first and third sets in this example, did this initially so I wasn't opening with the bad repetition)
Aaannd I'm still completely stuck on how to form an approach for n or even 4 sets. It certainly gets trickier because there are now different sizes of contiguous groups to avoid, contiguous trios as well as pairs.. Any thoughts? Am I crazy for even trying to do this?
Even after the modifications in your question, I'm still not sure exactly what you want. It seems that what you would really like is impossible, but I'm not sure exactly how much relaxation in the conditions is acceptable. Nevertheless, I'll give it a crack.
Oddly there seems to be little literature (that I can find, anyway) covering the subject of your problem, so I had to invent something myself. This is the idea: you are looking for a sequence of points on a multidimensional torus such that elements of the sequence are as far apart as possible in a complicated metric. What this reminds me of is something I learned years ago in a mechanics class, strangely enough. If you have a line on a flat torus with rational slope, the line will loop back onto itself after a few cycles, but if you have a line with irrational slope, the line will densely cover the entire torus.
I don't expect that to mean a lot to many people, but it did give me an idea. The index for each set could step by an irrational amount. You would have to take the floor, of course, and then modulo whatever, but it does seem to cover the bases well, so to speak. The irrational step for each set could be different (and mutually irrational, to use rather loose language).
To make the idea more precise, I wrote a short program. Please check it out.
class Equidistributed {
static final double IRRATIONAL1 = Math.sqrt(2);
static final double IRRATIONAL2 = Math.sqrt(3);
static final double IRRATIONAL3 = Math.sqrt(5)-1;
// four sets of 7 elements each
static int setSize = 7;
public static void main(String[] args) {
for (int i = 0; i < Math.pow(setSize,4); i++) {
String tuple = "";
int j = i % setSize;
tuple += j + ",";
j = ((int)Math.floor(i*IRRATIONAL1)) % setSize;
tuple += j + ",";
j = ((int)Math.floor(i*IRRATIONAL2)) % setSize;
tuple += j + ",";
j = ((int)Math.floor(i*IRRATIONAL3)) % setSize;
tuple += j;
System.out.println(tuple);
}
}
}
I "eyeballed" the results, and they aren't perfect, but they're pretty nice. Plus the program runs quickly. It's for four sets with a variable number of elements (I chose 7 for the example). The irrational numbers I'm using are based on square roots of prime numbers; I subtracted 1 from sqrt(5) so that the result would be in the range between 1 and 2. Each tuple is basically
(i, floor(i*irrational1), floor(i*irrational2), floor(i*irrational3)) mod 7
Statistically that should make the sequence evenly distributed, which is a consequence of what you want. Whether that translates into the right "distance" properties, I can't really be sure. You should probably write a program to test whether a sequence has the property you want, and then pipe the output from my program into the test.
Define an array of all possible combinations.
For each possible order of the array, compute your distance score. If greater than the previous best (default start = 0), then copy the array to your output, overwriting the previous best array.
Assuming values are 1 dimensional, you do not need to compare the distance between every single element. Instead, you can find the maximum and minimum value within each set before comparing it with other sets.
Step 1: Find the element with maximum value and the minimum value within each set (eg A1, A34, B4, B32, C5, C40, with the smaller number with smaller values in this example)
Step 2: Compare A1 with the maximum values of all other sets, and repeat the process for all minimum values.
Generalized algorithm and wrote code to do performance testing:
import java.util.*;
public class Solution {
public static void main(String[] args) throws Exception {
List<String> A = new ArrayList<>();
A.add("A0"); A.add("A1"); A.add("A2");
A.add("A3"); A.add("A4"); A.add("A5"); A.add("A6");
List<String> B = new ArrayList<>();
B.add("B0"); B.add("B1"); B.add("B2");
B.add("B3"); B.add("B4"); B.add("B5"); B.add("B6");
List<String> C = new ArrayList<>();
C.add("C0"); C.add("C1"); C.add("C2");
C.add("C3"); C.add("C4"); C.add("C5"); C.add("C6");
List<String> D = new ArrayList<>();
D.add("D0"); D.add("D1"); D.add("D2");
D.add("D3"); D.add("D4"); D.add("D5"); D.add("D6");
List<List<String>> columns = new ArrayList<>();
columns.add(A); columns.add(B); columns.add(C); columns.add(D);
List<String> output = equidistribute(columns);
// for (String row : output) {
// System.out.println(row);
// }
// new Solution().test(output, columns.size(), A.size());
new Solution().testAllTheThings();
}
public static List<String> equidistribute(List<List<String>> columns) {
List<String> output = new ArrayList<>();
int[] primeNumbers = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
443, 449, 457, 461, 463, 467, 479, 487, 491, 499,
503, 509, 521, 523, 541};
int numberOfColumns = columns.size();
int numberOfElements = columns.get(0).size();
for (int i = 0; i < Math.pow(numberOfElements, numberOfColumns); i++) {
String row = "";
for (int j = 0; j < numberOfColumns; j++) {
if (j==0) {
row += columns.get(0).get(i % numberOfElements);
} else {
int index = ((int) Math.floor(i * Math.sqrt(primeNumbers[j-1]))) % numberOfElements;
row += " " + columns.get(j).get(index);
}
}
output.add(row);
}
return output;
}
class MutableInt {
int value = 0;
public void increment() { value++; }
public int get() { return value; }
public String toString() { return String.valueOf(value); }
}
public void test(List<String> columns, int numberOfColumns, int numberOfElements) throws Exception {
List<HashMap<String, MutableInt>> pairMaps = new ArrayList<>();
List<HashMap<String, MutableInt>> individualElementMaps = new ArrayList<>();
// initialize structures for calculating min distance
for (int i = 0; i < numberOfColumns; i++) {
if (i != numberOfColumns-1) {
HashMap<String, MutableInt> pairMap = new HashMap<>();
pairMaps.add(pairMap);
}
HashMap<String, MutableInt> individualElementMap = new HashMap<>();
individualElementMaps.add(individualElementMap);
}
int minDistancePair = Integer.MAX_VALUE;
int minDistanceElement = Integer.MAX_VALUE;
String pairOutputMessage = "";
String pairOutputDebugMessage = "";
String elementOutputMessage = "";
String elementOutputDebugMessage = "";
String outputMessage = numberOfColumns + " columns, " + numberOfElements + " elements";
for (int i = 0; i < columns.size(); i++) {
String[] elements = columns.get(i).split(" ");
for (int j = 0; j < numberOfColumns; j++) {
// pair stuff
if (j != numberOfColumns-1) {
String pairEntry = elements[j] + " " + elements[j+1];
MutableInt count = pairMaps.get(j).get(pairEntry);
if (pairMaps.get(j).containsKey(pairEntry)) {
if (count.get() <= minDistancePair) {
minDistancePair = count.get();
pairOutputMessage = "minDistancePair = " + minDistancePair;
pairOutputDebugMessage += "(" + pairEntry + " at line " + (i+1) + ") min = " + minDistancePair + "\n";
}
count = null;
}
if (count == null) {
pairMaps.get(j).put(pairEntry, new MutableInt());
}
}
// element stuff
String elementEntry = elements[j];
MutableInt count = individualElementMaps.get(j).get(elementEntry);
if (individualElementMaps.get(j).containsKey(elementEntry)) {
if (count.get() <= minDistanceElement) {
minDistanceElement = count.get();
elementOutputMessage = "minDistanceElement = " + minDistanceElement;
elementOutputDebugMessage += "(" + elementEntry + " at line " + (i+1) + ") min = " + minDistanceElement + "\n";
}
count = null;
}
if (count == null) {
individualElementMaps.get(j).put(elementEntry, new MutableInt());
}
}
// increment counters
for (HashMap<String, MutableInt> pairMap : pairMaps) {
Iterator it = pairMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry mapEntry = (Map.Entry) it.next();
((MutableInt) mapEntry.getValue()).increment();
}
}
for (HashMap<String, MutableInt> elementMap : individualElementMaps) {
Iterator it = elementMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry mapEntry = (Map.Entry) it.next();
((MutableInt) mapEntry.getValue()).increment();
}
}
}
System.out.println(outputMessage + " -- " + pairOutputMessage + ", " + elementOutputMessage);
// System.out.print(elementOutputDebugMessage);
// System.out.print(pairOutputDebugMessage);
}
public void testAllTheThings() throws Exception {
char[] columnPrefix = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
int maxNumberOfColumns = columnPrefix.length;
int maxNumberOfElements = 30;
for (int i = 2; i < maxNumberOfColumns; i++) {
for (int j = i; j < maxNumberOfElements; j++) {
List<List<String>> columns = new ArrayList<>();
for (int k = 0; k < i; k++) {
List<String> column = new ArrayList<>();
for (int l = 0; l < j; l++) {
column.add(String.valueOf(columnPrefix[k]) + l);
}
columns.add(column);
}
List<String> output = equidistribute(columns);
test(output, i, j);
}
}
}
}
edit: removed restriction that each set must have same number of elements
public List<String> equidistribute(List<List<String>> columns) {
List<String> output = new ArrayList<>();
int[] primeNumbers = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
443, 449, 457, 461, 463, 467, 479, 487, 491, 499,
503, 509, 521, 523, 541};
int numberOfColumns = columns.size();
int numberOfCombinations = 1;
for (List<String> column : columns) {
numberOfCombinations *= column.size();
}
for (int i = 0; i < numberOfCombinations; i++) {
String row = "";
for (int j = 0; j < numberOfColumns; j++) {
int numberOfElementsInColumn = columns.get(j).size();
if (j==0) {
row += columns.get(0).get(i % numberOfElementsInColumn);
} else {
int index = ((int) Math.floor(i * Math.sqrt(primeNumbers[j-1]))) % numberOfElementsInColumn;
row += "|" + columns.get(j).get(index);
}
}
output.add(row);
}
return output;
}
I'm working on calling a third-party DLL from my Perl project using XS, under Cygwin on Windows using g++. One of the DLL functions takes a struct as an argument and returns its main results in a pointer to a struct. For now I pass in a flat list of 28 integers and populate the first struct. Then I call the function. Then I want to flatten the resulting struct into a list of up to 54 integers.
(This seems like a lot of integers, but the DLL function is quite complex and takes a long time to run, so I think it's worth it. Unless someone has a better idea?)
This is close to working. I can tell that the results are mostly sensible. But there are two bizarre problems.
When I print out the same variables, I get different results depending on whether it's in a 'for' loop or not! I show this below. I've stared at this so long now.
I get "Out of memory" as soon as I get to the first XPUSHs.
Here is the XS code.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "dll.h"
MODULE = Bridge::Solver::DDS_IF PACKAGE = Bridge::Solver::DDS_IF
PROTOTYPES: ENABLE
void
SolveBoard(inlist)
SV * inlist
INIT:
struct deal dl;
struct futureTricks fut;
int target, solutions, mode, thrId;
int i, j, ret;
if ((! SvROK(inlist)) ||
(SvTYPE(SvRV(inlist)) != SVt_PVAV) ||
av_len((AV *) SvRV(inlist)) != 27)
{
XSRETURN_UNDEF;
}
printf("New INIT OK\n");
PPCODE:
dl.trump = SvIV(*av_fetch((AV *)SvRV(inlist), 0, 0));
dl.first = SvIV(*av_fetch((AV *)SvRV(inlist), 1, 0));
dl.currentTrickSuit[0] = SvIV(*av_fetch((AV *)SvRV(inlist), 2, 0));
dl.currentTrickSuit[1] = SvIV(*av_fetch((AV *)SvRV(inlist), 3, 0));
dl.currentTrickSuit[2] = SvIV(*av_fetch((AV *)SvRV(inlist), 4, 0));
dl.currentTrickRank[0] = SvIV(*av_fetch((AV *)SvRV(inlist), 5, 0));
dl.currentTrickRank[1] = SvIV(*av_fetch((AV *)SvRV(inlist), 6, 0));
dl.currentTrickRank[2] = SvIV(*av_fetch((AV *)SvRV(inlist), 7, 0));
dl.remainCards[0][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 8, 0));
dl.remainCards[0][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 9, 0));
dl.remainCards[0][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 10, 0));
dl.remainCards[0][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 11, 0));
dl.remainCards[1][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 12, 0));
dl.remainCards[1][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 13, 0));
dl.remainCards[1][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 14, 0));
dl.remainCards[1][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 15, 0));
dl.remainCards[2][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 16, 0));
dl.remainCards[2][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 17, 0));
dl.remainCards[2][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 18, 0));
dl.remainCards[2][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 19, 0));
dl.remainCards[3][0] = SvUV(*av_fetch((AV *)SvRV(inlist), 20, 0));
dl.remainCards[3][1] = SvUV(*av_fetch((AV *)SvRV(inlist), 21, 0));
dl.remainCards[3][2] = SvUV(*av_fetch((AV *)SvRV(inlist), 22, 0));
dl.remainCards[3][3] = SvUV(*av_fetch((AV *)SvRV(inlist), 23, 0));
target = SvIV(*av_fetch((AV *)SvRV(inlist), 24, 0));
solutions = SvIV(*av_fetch((AV *)SvRV(inlist), 25, 0));
mode = SvIV(*av_fetch((AV *)SvRV(inlist), 26, 0));
thrId = SvIV(*av_fetch((AV *)SvRV(inlist), 27, 0));
ret = SolveBoard(dl, target, solutions, mode, &fut, thrId);
printf("Return code %d\n", ret);
printf("Nodes %d\n", fut.nodes);
printf("Cards %d\n", fut.cards);
printf("%6s %12s %12s %12s %12s\n",
"", "suit", "rank", "equals", "score");
printf("%6d %12d %12d %12d %12d\n\n",
0, fut.suit[0], fut.rank[0], fut.equals[0], fut.score[0]);
for (i = 0; i < 13; i++)
{
printf("%6d %12d %12d %12d %12d\n",
i, fut.suit[i], fut.rank[i], fut.equals[i], fut.score[i]);
}
printf("\n%6d %12d %12d %12d %12d\n\n",
0, fut.suit[0], fut.rank[0], fut.equals[0], fut.score[0]);
printf("Trying to push nodes\n");
XPUSHs(sv_2mortal(newSViv(fut.nodes)));
printf("Trying to push cards\n");
XPUSHs(sv_2mortal(newSViv(fut.cards)));
printf("Trying to loop\n");
for (i = 0; i <= 12; i++)
{
XPUSHs(sv_2mortal(newSViv(fut.suit [i])));
XPUSHs(sv_2mortal(newSViv(fut.rank [i])));
XPUSHs(sv_2mortal(newSViv(fut.equals[i])));
XPUSHs(sv_2mortal(newSViv(fut.score [i])));
}
printf("Done looping\n");
Here is the relevant part of the DLL header file.
struct futureTricks
{
int nodes;
int cards;
int suit[13];
int rank[13];
int equals[13];
int score[13];
};
struct deal
{
int trump;
int first;
int currentTrickSuit[3];
int currentTrickRank[3];
unsigned int remainCards[4][4];
};
extern "C" int SolveBoard(
struct deal dl,
int target,
int solutions,
int mode,
struct futureTricks *futp,
int threadIndex);
And here is the output. The return code is OK. The nodes and cards are not. If you squint, you might notice that 0 and 768 also occur within the output table, so maybe there's some kind of offset going on.
The first bizarre thing is that the two '0' lines before and after the main table are different from the '0' line in the main table. The data in the main table is as expected, though, including the garbage in lines 10-12.
The second problem is that XPUSHs doesn't do as intended.
New INIT OK
Return code 1
Nodes 0
Cards 768
suit rank equals score
0 0 2 -2147319000 -2147296756
0 2 2 0 2
1 2 6 0 2
2 2 10 768 2
3 2 13 0 2
4 3 14 0 2
5 0 6 0 1
6 0 10 512 1
7 0 13 0 1
8 3 4 0 0
9 3 11 0 0
10 1773292640 -2147056120 4 -2147319000
11 1772354411 0 -2146989552 -2146837752
12 8192 35 2665016 -2147319000
0 0 2 -2147319000 -2147296756
Trying to push nodes
Out of memory!
It was indeed a problem with the stack.
The supplied dll.h tested _WIN32 and #define'd STDCALL to __stdcall under _WIN32, otherwise to empty.
g++ under Cygwin does not emit _WIN32, so I guess the calling convention defaulted to __cdecl.
Manually defining _WIN32 created lots of other errors, but instead I added to dll.h a test for \__CYGWIN__, which the compiler does emit, and gave it to the author for his next release.
A very frustrating error to find, so I hope this might help somebody else in the future. You never know...
with the offset problem, there may be because Perl messes pretty bad with C variable definitions.
including dll.h before all others will probably solve that.
I've been working with this variation of dynamic programming to solve a knapsack problem:
KnapsackItem = Struct.new(:name, :cost, :value)
KnapsackProblem = Struct.new(:items, :max_cost)
def dynamic_programming_knapsack(problem)
num_items = problem.items.size
items = problem.items
max_cost = problem.max_cost
cost_matrix = zeros(num_items, max_cost+1)
num_items.times do |i|
(max_cost + 1).times do |j|
if(items[i].cost > j)
cost_matrix[i][j] = cost_matrix[i-1][j]
else
cost_matrix[i][j] = [cost_matrix[i-1][j], items[i].value + cost_matrix[i-1][j-items[i].cost]].max
end
end
end
cost_matrix
end
def get_used_items(problem, cost_matrix)
i = cost_matrix.size - 1
currentCost = cost_matrix[0].size - 1
marked = Array.new(cost_matrix.size, 0)
while(i >= 0 && currentCost >= 0)
if(i == 0 && cost_matrix[i][currentCost] > 0 ) || (cost_matrix[i][currentCost] != cost_matrix[i-1][currentCost])
marked[i] = 1
currentCost -= problem.items[i].cost
end
i -= 1
end
marked
end
This has worked great for the structure above where you simply provide a name, cost and value. Items can be created like the following:
items = [
KnapsackItem.new('david lee', 8000, 30) ,
KnapsackItem.new('kevin love', 12000, 50),
KnapsackItem.new('kemba walker', 7300, 10),
KnapsackItem.new('jrue holiday', 12300, 30),
KnapsackItem.new('stephen curry', 10300, 80),
KnapsackItem.new('lebron james', 5300, 90),
KnapsackItem.new('kevin durant', 2300, 30),
KnapsackItem.new('russell westbrook', 9300, 30),
KnapsackItem.new('kevin martin', 8300, 15),
KnapsackItem.new('steve nash', 4300, 15),
KnapsackItem.new('kyle lowry', 6300, 20),
KnapsackItem.new('monta ellis', 8300, 30),
KnapsackItem.new('dirk nowitzki', 7300, 25),
KnapsackItem.new('david lee', 9500, 35),
KnapsackItem.new('klay thompson', 6800, 28)
]
problem = KnapsackProblem.new(items, 65000)
Now, the problem I'm having is that I need to add a position for each of these players and I have to let the knapsack algorithm know that it still needs to maximize value across all players, except there is a new restriction and that restriction is each player has a position and each position can only be selected a certain amount of times. Some positions can be selected twice, others once. Items would ideally become this:
KnapsackItem = Struct.new(:name, :cost, :position, :value)
Positions would have a restriction such as the following:
PositionLimits = Struct.new(:position, :max)
Limits would be instantiated perhaps like the following:
limits = [Struct.new('PG', 2), Struct.new('C', 1), Struct.new('SF', 2), Struct.new('PF', 2), Struct.new('Util', 2)]
What makes this a little more tricky is every player can be in the Util position. If we want to disable the Util position, we will just set the 2 to 0.
Our original items array would look something like the following:
items = [
KnapsackItem.new('david lee', 'PF', 8000, 30) ,
KnapsackItem.new('kevin love', 'C', 12000, 50),
KnapsackItem.new('kemba walker', 'PG', 7300, 10),
... etc ...
]
How can position restrictions be added to the knapsack algorithm in order to still retain max value for the provided player pool provided?
There are some efficient libraries available in ruby which could suit your task , Its clear that you are looking for some constrain based optimization , there are some libraries in ruby which are a opensource so, free to use , Just include them in you project. All you need to do is generate Linear programming model objective function out of your constrains and library's optimizer would generate Solution which satisfy all your constrains , or says no solution exists if nothing can be concluded out of the given constrains .
Some such libraries available in ruby are
RGLPK
OPL
LP Solve
OPL follows the LP syntax similar to IBM CPLEX , which is widely used Optimization software, So you could get good references on how to model the LP using this , Moreover this is build on top of the RGLPK.
As I understand, the additional constraint that you are specifying is as following:
There shall be a set of elements, out which only at most k (k = 1 or
2) elements can be selected in the solution. There shall be multiple
such sets.
There are two approaches that come to my mind, neither of which are efficient enough.
Approach 1:
Divide the elements into groups of positions. So if there are 5 positions, then each element shall be assigned to one of 5 groups.
Iterate (or recur) through all the combinations by selecting 1 (or 2) element from each group and checking the total value and cost. There are ways in which you can fathom some combinations. For example, in a group if there are two elements in which one gives more value at lesser cost, then the other can be rejected from all solutions.
Approach 2:
Mixed Integer Linear Programming Approach.
Formulate the problem as follows:
Maximize summation (ViXi) {i = 1 to N}
where Vi is value and
Xi is a 1/0 variable denoting presence/absence of an element from the solution.
Subject to constraints:
summation (ciXi) <= C_MAX {total cost}
And for each group:
summation (Xj) <= 1 (or 2 depending on position)
All Xi = 0 or 1.
And then you will have to find a solver to solve the above MILP.
This problem is similar to a constraint vehicle routing problem. You can try a heuristic like the saving algorithm from Clarke&Wright. You can also try a brute-force algorithm with less players.
Considering players have Five positions your knapsack problem would be:-
Knpsk(W,N,PG,C,SF,PF,Util) = max(Knpsk(W-Cost[N],N-1,...)+Value[N],Knpsk(W,N-1,PG,C,SF,PF,Util),Knpsk(W-Cost[N],N-1,PG,C,SF,PF,Util-1)+Value[N])
if(Pos[N]=="PG") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG-1,....)
if(Pos[N]=="C") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG,C-1....)
so on...
PG,C,SF,PF,Util are current position capacities
W is current knapsack capacity
N number of items available
Dynamic Programming can be used as before using 7-D table and as in your case the values of positions are small it will slow down algorithm by factor of 16 which is great for n-p complete problem
Following is dynamic programming solution in JAVA:
public class KnapsackSolver {
HashMap CostMatrix;
// Maximum capacities for positions
int posCapacity[] = {2,1,2,2,2};
// Total positions
String[] positions = {"PG","C","SF","PF","util"};
ArrayList playerSet = new ArrayList<player>();
public ArrayList solutionSet;
public int bestCost;
class player {
int value;
int cost;
int pos;
String name;
public player(int value,int cost,int pos,String name) {
this.value = value;
this.cost = cost;
this.pos = pos;
this.name = name;
}
public String toString() {
return("'"+name+"'"+", "+value+", "+cost+", "+positions[pos]);
}
}
// Used to add player to list of available players
void additem(String name,int cost,int value,String pos) {
int i;
for(i=0;i<positions.length;i++) {
if(pos.equals(positions[i]))
break;
}
playerSet.add(new player(value,cost,i,name));
}
// Converts subproblem data to string for hashing
public String encode(int Capacity,int Totalitems,int[] positions) {
String Data = Capacity+","+Totalitems;
for(int i=0;i<positions.length;i++) {
Data = Data + "," + positions[i];
}
return(Data);
}
// Check if subproblem is in hash tables
int isDone(int capacity,int players,int[] positions) {
String k = encode(capacity,players,positions);
if(CostMatrix.containsKey(k)) {
//System.out.println("Key found: "+k+" "+(Integer)CostMatrix.get(k));
return((Integer)CostMatrix.get(k));
}
return(-1);
}
// Adds subproblem added hash table
void addEncode(int capacity,int players,int[] positions,int value) {
String k = encode(capacity,players,positions);
CostMatrix.put(k, value);
}
boolean checkvalid(int capacity,int players) {
return(!(capacity<1||players<0));
}
// Solve the Knapsack recursively with Hash look up
int solve(int capacity,int players,int[] posCapacity) {
// Check if sub problem is valid
if(checkvalid(capacity,players)) {
//System.out.println("Processing: "+encode(capacity,players,posCapacity));
player current = (player)playerSet.get(players);
int sum1 = 0,sum2 = 0,sum3 = 0;
int temp = isDone(capacity,players-1,posCapacity);
// Donot add player
if(temp>-1) {
sum1 = temp;
}
else sum1 = solve(capacity,players-1,posCapacity);
//check if current player can be added to knapsack
if(capacity>=current.cost) {
posCapacity[posCapacity.length-1]--;
temp = isDone(capacity-current.cost,players-1,posCapacity);
posCapacity[posCapacity.length-1]++;
// Add player to util
if(posCapacity[posCapacity.length-1]>0) {
if(temp>-1) {
sum2 = temp+current.value;
}
else {
posCapacity[posCapacity.length-1]--;
sum2 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
posCapacity[posCapacity.length-1]++;
}
}
// Add player at its position
int i = current.pos;
if(posCapacity[i]>0) {
posCapacity[i]--;
temp = isDone(capacity-current.cost,players-1,posCapacity);
posCapacity[i]++;
if(temp>-1) {
sum3 = temp+current.value;
}
else {
posCapacity[i]--;
sum3 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
posCapacity[i]++;
}
}
}
//System.out.println(sum1+ " "+ sum2+ " " + sum3 );
// Evaluate the maximum of all subproblem
int res = Math.max(Math.max(sum1,sum2), sum3);
//add current solution to Hash table
addEncode(capacity, players, posCapacity,res);
//System.out.println("Encoding: "+encode(capacity,players,posCapacity)+" Cost: "+res);
return(res);
}
return(0);
}
void getSolution(int capacity,int players,int[] posCapacity) {
if(players>=0) {
player curr = (player)playerSet.get(players);
int bestcost = isDone(capacity,players,posCapacity);
int sum1 = 0,sum2 = 0,sum3 = 0;
//System.out.println(encode(capacity,players-1,posCapacity)+" "+bestcost);
sum1 = isDone(capacity,players-1,posCapacity);
posCapacity[posCapacity.length-1]--;
sum2 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
posCapacity[posCapacity.length-1]++;
posCapacity[curr.pos]--;
sum3 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
posCapacity[curr.pos]++;
if(bestcost==0)
return;
// Check if player is not added
if(sum1==bestcost) {
getSolution(capacity,players-1,posCapacity);
}
// Check if player is added to util
else if(sum2==bestcost) {
solutionSet.add(curr);
//System.out.println(positions[posCapacity.length-1]+" added");
posCapacity[posCapacity.length-1]--;
getSolution(capacity-curr.cost,players-1,posCapacity);
posCapacity[posCapacity.length-1]++;
}
else {
solutionSet.add(curr);
//System.out.println(positions[curr.pos]+" added");
posCapacity[curr.pos]--;
getSolution(capacity-curr.cost,players-1,posCapacity);
posCapacity[curr.pos]++;
}
}
}
void getOptSet(int capacity) {
CostMatrix = new HashMap<String,Integer>();
bestCost = solve(capacity,playerSet.size()-1,posCapacity);
solutionSet = new ArrayList<player>();
getSolution(capacity, playerSet.size()-1, posCapacity);
}
public static void main(String[] args) {
KnapsackSolver ks = new KnapsackSolver();
ks.additem("david lee", 8000, 30, "PG");
ks.additem("kevin love", 12000, 50, "C");
ks.additem("kemba walker", 7300, 10, "SF");
ks.additem("jrue holiday", 12300, 30, "PF");
ks.additem("stephen curry", 10300, 80, "PG");
ks.additem("lebron james", 5300, 90, "PG");
ks.additem("kevin durant", 2300, 30, "C");
ks.additem("russell westbrook", 9300, 30, "SF");
ks.additem("kevin martin", 8300, 15, "PF");
ks.additem("steve nash", 4300, 15, "C");
ks.additem("kyle lowry", 6300, 20, "PG");
ks.additem("monta ellis", 8300, 30, "C");
ks.additem("dirk nowitzki", 7300, 25, "SF");
ks.additem("david lee", 9500, 35, "PF");
ks.additem("klay thompson", 6800, 28,"PG");
//System.out.println("Items added...");
// System.out.println(ks.playerSet);
int maxCost = 30000;
ks.getOptSet(maxCost);
System.out.println("Best Value: "+ks.bestCost);
System.out.println("Solution Set: "+ks.solutionSet);
}
}
Note: If players with certain positions are added more than its capacity then those added as util because players from any position can be added to util.
In a bitmap reader I found and modified, the first few colors of some images are innacurate by a large amount, in other images it reads perfectly, in just now, the first 7 or so colors of an image that I must have it read are not accurate at all. I don't understand byte orders, so please help!
Heres my modified copy of the code:
---
-- (Evil Steve)Because I'm a kind and wonderful person: http://www.gamedev.net/topic/572784-lua-read-bitmap/
---
function error(err)
-- Replace with your own error output method:
print(err);
end
-- Helper function: Parse a 16-bit WORD from the binary string
function ReadWORD(str, offset)
local loByte = str:byte(offset);
local hiByte = str:byte(offset+1);
return hiByte*256 + loByte;
end
-- Helper function: Parse a 32-bit DWORD from the binary string
function ReadDWORD(str, offset)
local loWord = ReadWORD(str, offset);
local hiWord = ReadWORD(str, offset+2);
return hiWord*65536 + loWord;
end
-- Process a bitmap file in a string, and call DrawPoint for each pixel
function OpenBitmap(File, Stream)
if Stream == nil then Stream = false end
local bytecode = File:read("*a")
-------------------------
-- Parse BITMAPFILEHEADER
-------------------------
local offset = 1;
local bfType = ReadWORD(bytecode, offset);
if(bfType ~= 0x4D42) then
error("Not a bitmap file (Invalid BMP magic value)");
return;
end
local bfOffBits = ReadWORD(bytecode, offset+10);
-------------------------
-- Parse BITMAPINFOHEADER
-------------------------
offset = 15; -- BITMAPFILEHEADER is 14 bytes long
local biWidth = ReadDWORD(bytecode, offset+4);
local biHeight = ReadDWORD(bytecode, offset+8);
local biBitCount = ReadWORD(bytecode, offset+14);
local biCompression = ReadDWORD(bytecode, offset+16);
if(biBitCount ~= 24) then
error("Only 24-bit bitmaps supported (Is " .. biBitCount .. "bpp)");
return;
end
if(biCompression ~= 0) then
error("Only uncompressed bitmaps supported (Compression type is " .. biCompression .. ")");
return;
end
---------------------
-- Parse bitmap image
---------------------
local TmpImg = {}
if Stream == false then
for y = biHeight-1, 0, -1 do
offset = bfOffBits + (biWidth*biBitCount/8)*y + 1;
for x = 0, biWidth-1 do
local b = bytecode:byte(offset);
local g = bytecode:byte(offset+1);
local r = bytecode:byte(offset+2);
offset = offset + 3;
TmpImg[#TmpImg+1] = {r,g,b}
end
end
else
for y = biHeight-1, 0, -1 do
offset = bfOffBits + (biWidth*biBitCount/8)*y + 1;
for x = 0, biWidth-1 do
local b = bytecode:byte(offset);
local g = bytecode:byte(offset+1);
local r = bytecode:byte(offset+2);
offset = offset + 3;
TmpImg[#TmpImg+1] = r
TmpImg[#TmpImg+1] = g
TmpImg[#TmpImg+1] = b
end
end
end
return TmpImg, biWidth, biHeight
end
function OpenBmp(FileName, Stream)
if Stream == nil then Stream = false end
if FileName == nil then
return false
end
local File = assert(io.open(FileName, 'rb'))
local Data, Width, Height = OpenBitmap(File, Stream)
File:close()
return Data, Width, Height
end
I cannot give you the code I run with this, sadly, because it has too many dependencies to bother with, but its output is:
<254, 254, 254, 256>
<99, 254, 254, 256>
<49, 74, 91, 256>
When ran with the following bmp colors:
<90, 106, 113, 256>
<188, 194, 197, 256>
<254, 254, 254, 256>
I don't see any pattern, and the bmp reader seems to make sense, it prints no errors when reading, and I made sure to save the bmp as 24 bit as required. all help appreciated :-)
In the example above, offset wasn't considering that row widths must always be a multiple of 4 bytes wide, with it padded if it was below the multiple of 4. You can resolve this by rounding up row width to the nearest multiple of 4, which explains why the function sometimes read images accurately, and other times not.
the behavior from the pixels in the beginning being false, but the rest later on being accurate was due to creeping, logically, the first would be accurate, with the last inaccurate, but the creeping went the opposite way due to bitmaps being read bottom-up and right-left.
Let's say I want to check if a number n = 123 has duplicate digits. I tried:
#include <iostream>
using namespace std;
int main() {
int n = 123;
int d1 = n % 10;
int d2 = ( n / 10 ) % 10;
int d3 = ( n / 100 ) % 10;
if( d1 != d2 && d1 != d3 && d2 != d3 ) {
cout << n << " does not have duplicate digits.\n";
}
}
Is there any faster solution to this problem?
Update
Sorry for being unclear. The code above was written in C++ only for description purpose. I have to solve this problem in TI-89, with a number of 9 digits. And since the limitation of memory and speed, I'm looking for a fastest way possible.
TI-89 only has several condition keyword:
If
If ... Then
when(
For ... EndFor
While ... EndWhile
Loop ... EndLoop
Custom ... EndCustom
Thanks,
Chan
Not necessarily faster but you should measure anyway, just in case - my optimisation mantra is "measure, don't guess".
But I believe it's clearer in intent (and simple enough to be translated to a simpler calculator language. It's also able to handle arbitrarily sized integers.
int hasDupes (unsigned int n) {
// Flag to indicate digit has been used, all zero to start.
int used[10] = {0};
// More than 10 digits must have duplicates, return true quickly.
if (n > 9999999999) return 1;
// Process each digit in number.
while (n != 0) {
// If duplicate, return true as soon as found.
if (used[n%10]) return 1;
// Otherwise, mark used, go to next digit.
used[n%10] = 1;
n /= 10;
}
// No duplicates after checking all digits, return false.
return 0;
}
If you have a limited range of possibilities, you can use the time-honoured approach of sacrificing space for time. For example, let's say you're talking about numbers between 0 and 999 inclusive (the : : markers simply indicate data I've removed to keep the size of the answer manageable):
const int *hasDupes = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 9
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 10 - 19
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 20 - 29
: :
0, 0, 1, 0, 0, 1, 0, 0, 0, 0, // 520 - 529
: :
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, // 810 - 819
: :
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, // 970 - 979
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, // 980 - 989
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 990 - 999
};
and just do a table lookup of hasDupes[n]. The table itself could be generated (once) programmatically and then just inserted into your code for usage.
However, based on your edit where you state you need to handle nine-digit numbers, a billion-element array is probably not going to be possible on your calculator. I would therefore opt for the first solution.
template<class T, int radix = 10>
bool has_duplicate_digits(T n) {
int digits_mask = 0;
while (digits_mask |= (1 << (n % radix)), n /= radix)
if (digits_mask & (1 << (n % radix)))
return true;
return false;
}
Something like that should work as long as n is nonnegative and int has at least radix bits.
digits_mask is a bitset (bit 0 represents the occurrence of a 0 digit, bit 1 represents the occurrence of a 1 digit, etc.).
The bitmap is populated with the least significant digit of n, and the rest of the digits are shifted down. If there are more digits, and the new least significant digit is marked as having occurred previously, return true, otherwise repeat.
When there are no more digits, return false.
1 << x returns 1, 2, 4, 8, etc.: masks to use to test/set bits in the bitset.
a |= z is shorthand for a = a | z, which sets bits by the union of a from z.
a & z is the intersection of the bits in a and z, and is zero (false) if none are set and non-zero (true) if any are set.
I did a crash course in TI-89 basic to answer :)
Let's see if this works (I haven't an emulator, so can't check).
Test()
Prgm
{0,0,0,0,0,0,0,0,0,0}->A
Title "Request"
Request "Enter a number",B
EndDlog
Expr(B)->B
While B > 1
MOD(10,B)->C
if A[C+1] = 1 goto K
1->A[C+1]
B-C->B
EndWhile
Title "Done"
Text "Numbers non repeating"
Enddlog
goto J
Lbl K
Title "Done"
Text "Numbers repeating"
Enddlog
Lbl J
EndPrgm