Iteration run faster for "even teams" code - java

I am coding to create two "even teams" based on the players' scores (puntajes).
The algorithm runs through the array of players and compares the score of each one to get a minimum difference and then sorts players into two arrays, one for each team.
Here is my code:
if (listaDeJugadores.size() == 6)
//In this case I'm looking for a 6 player array, to create 3 vs 3 teams, but I'm looking to do until 22 (11 vs 11). Any ideas are welcomed.
{
int dif1 = Math.abs((listaDeJugadores.get(0).getPuntaje() + listaDeJugadores.get(1).getPuntaje() + listaDeJugadores.get(2).getPuntaje())
- (listaDeJugadores.get(3).getPuntaje() + listaDeJugadores.get(4).getPuntaje() + listaDeJugadores.get(5).getPuntaje()));
int jugador1 = 0;
int jugador2 = 1;
int jugador3 = 2;
int jugador4 = 3;
int jugador5 = 4;
int jugador6 = 5;
int a = 0;
int b = 0;
int c = 0;
//The two fors are to search the arrays. The iterador is to find the other three remaining positions to compare.
for (int cont2 = 1; cont2 < listaDeJugadores.size() - 1; cont2++) {
for (int cont3 = cont2 + 1; cont3 < listaDeJugadores.size(); cont3++) {
ArrayList<Integer> arr = new ArrayList<>();
int iterador[] = {0,1,2,3,4,5,6};
int j = 1;
for (int i=0;i<iterador.length;i++)
{
//I look for the missing players to compare from the 6 possible
if (cont2==iterador[i]|cont3==iterador[i])
{
j++;
}
else
{
c=b;
b=a;
a=j;
i--;
j++;
}
}
int dif = Math.abs((listaDeJugadores.get(0).getPuntaje() + listaDeJugadores.get(cont2).getPuntaje() + listaDeJugadores.get(cont3).getPuntaje())
- (listaDeJugadores.get(a).getPuntaje() + listaDeJugadores.get(b).getPuntaje() + listaDeJugadores.get(c).getPuntaje()));
if (dif < dif1) {
dif = dif1;
jugador1 = 0;
jugador2 = cont2;
jugador3 = cont3;
jugador4 = a;
jugador5 = b;
jugador6 = c;
}
}
}
//I add the best available sorted teams to EquipoBlanco or EquipoNegro.
listaEquipoBlanco.add(listaDeJugadores.get(jugador1));
listaEquipoBlanco.add(listaDeJugadores.get(jugador2));
listaEquipoBlanco.add(listaDeJugadores.get(jugador3));
listaEquipoNegro.add(listaDeJugadores.get(jugador4));
listaEquipoNegro.add(listaDeJugadores.get(jugador5));
listaEquipoNegro.add(listaDeJugadores.get(jugador6));
team1.setText("Equipo Blanco: " + (listaEquipoBlanco.get(0).getPuntaje() + listaEquipoBlanco.get(1).getPuntaje() + listaEquipoBlanco.get(2).getPuntaje()));
team2.setText("Equipo Negro: " + (listaEquipoNegro.get(0).getPuntaje() + listaEquipoNegro.get(1).getPuntaje() + listaEquipoNegro.get(2).getPuntaje()));
I think the code is ok, but when I try to run it, it won't open because it has really bad performance. I'm thinking I might have iterated to infinity or something similar, but also when I look at it and see fors inside of fors inside of fors I know something is wrong.
How can I make it run faster and have better performance?

A quick look at it and that inner for loop looks suspicious. I might be wrong without trying ( bad me), but it has a i--; in there and i is the loop index so of that happens all the time or often enough you will never exit that one.
That happens when this isn't true: cont2==iterador[i]|cont3==iterador[i] (bitwise or, should probably be logical or || by the way), Not sure that is guaranteed to be true at some point? Could go back and forth even perhaps. cont2 and contr3 doesn't change but i can change a little bit.
No protection for i to go below zero though either so could crash and burn (exception).

Related

How to randomly combine elements of 2 arrays while making sure to not reuse an element until all have been used at least once?

Essentially I'm writing a program that produces random poems out of an array of nouns and an array of adjectives.
This is accomplished basically using this line
String poem = adjectives[rand.nextInt(3)]+" "+ nouns[rand.nextInt(3)];
Simple enough, but I'm supposed to make sure that it doesn't reuse the same noun or adjective for the next poems until all of them have been used at least once already. I'm not sure how to do that.
Convert the arrays to list, so you can use Collections.shuffle to shuffle them. Once shuffled, you can then simply iterate over them. The values will be random order, and all words will be used exactly once. When you reach the end of an array of words, sort it again, and start from the beginning.
If a poem consists of 1 adjective + 1 noun as in your example, then the program could go something like this:
List<String> adjectives = new ArrayList<>(Arrays.asList(adjectivesArr));
List<String> nouns = new ArrayList<>(Arrays.asList(nounsArr));
Collections.shuffle(adjectives);
Collections.shuffle(nouns);
int aindex = 0;
int nindex = 0;
for (int i = 0; i < 100; ++i) {
String poem = adjectives.get(aindex++) + " " + nouns.get(nindex++);
System.out.println(poem);
if (aindex == adjectives.size()) {
aindex = 0;
Collections.shuffle(adjectives);
}
if (nindex == nouns.size()) {
nindex = 0;
Collections.shuffle(nouns);
}
}
The program will work with other number of adjectives and nouns per poem too.
If you must use an array, you can implement your own shuffle method, for example using the Fisher-Yates shuffle algorithm:
private void shuffle(String[] strings) {
Random random = new Random();
for (int i = strings.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
String temp = strings[i];
strings[i] = strings[index];
strings[index] = temp;
}
}
And then rewrite with arrays in terms of this helper shuffle function:
shuffle(adjectives);
shuffle(nouns);
int aindex = 0;
int nindex = 0;
for (int i = 0; i < 100; ++i) {
String poem = adjectives[aindex++] + " " + nouns[nindex++];
System.out.println(poem);
if (aindex == adjectives.length) {
aindex = 0;
shuffle(adjectives);
}
if (nindex == nouns.length) {
nindex = 0;
shuffle(nouns);
}
}
What you can do is make two more arrays, filled with boolean values, that correspond to the adjective and noun arrays. You can do something like this
boolean adjectiveUsed = new boolean[adjective.length];
boolean nounUsed = new boolean[noun.length];
int adjIndex, nounIndex;
By default all of the elements are initialized to false. You can then do this
adjIndex = rand.nextInt(3);
nounIndex = rand.nextInt(3);
while (adjectiveUsed[adjIndex])
adjIndex = rand.nextInt(3);
while (nounUsed[nounIndex]);
nounIndex = rand.nextInt(3);
Note, once all of the elements have been used, you must reset the boolean arrays to be filled with false again otherwise the while loops will run forever.
There are lots of good options for this. One is to just have a list of the words in random order that get used one by one and are then refreshed when empty.
private List<String> shuffledNouns = Collections.EMPTY_LIST;
private String getNoun() {
assert nouns.length > 0;
if (shuffledNouns.isEmpty()) {
shuffledNouns = new ArrayList<>(Arrays.asList(nouns));
Collections.shuffle(wordOrder);
}
return shuffledNouns.remove(0);
}
Best way to do this is to create a shuffled queue from each array, and then just start popping off the front of the queues to build your poems. Once the queues are empty you just generate new shuffled queues and start over. Here's a good shuffling algorithm:
https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
How about keeping two lists for the adjectives and nouns? You can use Collections.shuffle() to order them randomly.
import java.util.*;
class PoemGen {
static List<String> nouns = Arrays.asList("ball", "foobar", "dog");
static List<String> adjectives = Arrays.asList("slippery", "undulating", "crunchy");
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println(String.format("\nPoem %d", i));
generatePoem();
}
}
private static void generatePoem() {
Collections.shuffle(nouns);
Collections.shuffle(adjectives);
int nounIndex = nouns.size() - 1;
int adjectiveIndex = adjectives.size() - 1;
while (nounIndex >= 0 && adjectiveIndex >= 0) {
final String poem = adjectives.get(adjectiveIndex--)+" "+ nouns.get(nounIndex--);
System.out.println(poem);
}
}
}
Output:
Poem 0
crunchy dog
slippery ball
undulating foobar
Poem 1
undulating dog
crunchy ball
slippery foobar
Poem 2
slippery ball
crunchy dog
undulating foobar
Assuming you have the same number of noums and adjectives shuffle both arrays and then merge result. you can shuffle the arrays multiple times if you need (once you get to the end)
shuffleArray(adjectives);
shuffleArray(nouns);
for(int i=0;i<3;i++) {
String poem = adjectives[i] + " " + nouns[i];
}
A simple method to shuffle the arrays:
static void shuffleArray( String[] data) {
for (int i = data.length - 1; i > 0; i--) {
int index = rnd.nextInt(i + 1);
int aux = data[index];
data[index] = data[i];
data[i] = aux;
}
}
This might be overkill for this specific problem but it's an interesting alternative in my opinion:
You can use a linear congruential generator (LCG) to generate the random numbers instead of using rand.nextInt(3). An LCG gives you a pseudo-random sequence of numbers using this simple formula
nextNumber = (a * x + b) % m
Now comes the interesting part (which makes this work for your problem):
The Hull-Dobell-Theorem states that if your parameters a, b and m fit the following set of rules, the generator will generate every number between 0 and m-1 exactly once before repeating.
The conditions are:
m and the offset c are relatively prime
a - 1 is divisible by all prime factors of m
a - 1 is divisible by 4 if m is divisible by 4
This way you could generate your poems with exactly the same line of code as you currently have but instead just generate the array index with the LCG instead of rand.nextInt. This also means that this solution will give you the best performance, since there is no sorting, shuffling or searching involved.
Thanks for the responses everyone! This helped immeasurably. I am now officially traumatized by the sheer number of ways there are to solve even a simple problem.

Edit Distance solution for Large Strings

I'm trying to solve the edit distance problem. the code I've been using is below.
public static int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
// len1+1, len2+1, because finally return dp[len1][len2]
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= len2; j++) {
dp[0][j] = j;
}
//iterate though, and check last char
for (int i = 0; i < len1; i++) {
char c1 = word1.charAt(i);
for (int j = 0; j < len2; j++) {
char c2 = word2.charAt(j);
//if last two chars equal
if (c1 == c2) {
//update dp value for +1 length
dp[i + 1][j + 1] = dp[i][j];
} else {
int replace = dp[i][j] + 1 ;
int insert = dp[i][j + 1] + 1 ;
int delete = dp[i + 1][j] + 1 ;
int min = replace > insert ? insert : replace;
min = delete > min ? min : delete;
dp[i + 1][j + 1] = min;
}
}
}
return dp[len1][len2];
}
It's a DP approach. The problem it since it use a 2D array we cant solve this problem using above method for large strings. Ex: String length > 100000.
So Is there anyway to modify this algorithm to overcome that difficulty ?
NOTE:
The above code will accurately solve the Edit Distance problem for small strings. (which has length below 1000 or near)
As you can see in the code it uses a Java 2D Array "dp[][]" . So we can't initialize a 2D array for large rows and columns.
Ex : If i need to check 2 strings whose lengths are more than 100000
int[][] dp = new int[len1 + 1][len2 + 1];
the above will be
int[][] dp = new int[100000][100000];
So it will give a stackOverflow error.
So the above program only good for small length Strings.
What I'm asking is , Is there any way to solve this problem for large strings(length > 100000) efficiently in java.
First of all, there's no problem in allocating a 100k x 100k int array in Java, you just have to do it in the Heap, not the Stack (and on a machine with around 80GB of memory :))
Secondly, as a (very direct) hint:
Note that in your loop, you are only ever using 2 rows at a time - row i and row i+1. In fact, you calculate row i+1 from row i. Once you get i+1 you don't need to store row i anymore.
This neat trick allows you to store only 2 rows at the same time, bringing down the space complexity from n^2 to n. Since you stated that this is not homework (even though you're a CS undergrad by your profile...), I'll trust you to come up with the code yourself.
Come to think of it I recall having this exact problem when I was doing a class in my CS degree...

Java Statements execute except for changing of variable

So the premise here is that a String array of five "cards" is checked to see what sort of hand is given. The type of hand is an int where the higher number is a better hand. This particular segement of code was not written by me, but by a friend who is having trouble and I can't quite find the problem
int checkHand(String[][] Hand) {
boolean check = true;
for(int i = 0; i < 4; i++) {
if(!Hand[i][1].equals(Hand[i+1][1]))
check = false;
}
//check pair starts
for(int i = 0; i < 5; i++) {
for(int n = i + 1; n < 5; n++) {
if(Hand[i][0].equals(Hand[n][0])) {
BestCard = 1;//###OUTPUTS 1 FOR A PAIR###
temp = Hand[0];
Hand[0] = Hand[i];
Hand[i] = temp;
temp = Hand[1];
Hand[1] = Hand[n];
Hand[n] = temp;
//check three of a kind starts
for(int p = n + 1; p < 5; p++) {
if(Hand[i][0].equals(Hand[p][0])) {
temp = Hand[2];
Hand[2] = Hand[p];
Hand[p] = temp;
BestCard = 3; //Problem starts here- Output 3 for Three of a Kind
if(Hand[3][0].equals(Hand[4][0]))
BestCard = 6;//###OUTPUTS A 6 FOR A FULL HOUSE###
else if(Hand[2][0].equals(Hand[3][0])) {
BestCard = 7;
}
else if(Hand[2][0].equals(Hand[4][0])) {
temp = Hand[3];
Hand[3] = Hand[4];
Hand[4] = temp;
BestCard = 7;//###OUTPUTS A 7 FOR A FOUR OF A KIND###
}
}
}
}
}
}
if(check && BestCard<4)
BestCard = 5;//###OUTPUTS 5 FOR A FLUSH###
else if(check && BestCard == 4)
BestCard = 8;//###OUTPUTS 8 FOR A STRAIGHT FLUSH###
return BestCard;
}
When this runs (and there's more to it, but this is what I believe to be the problem area,) It can determine whether or not said hand is a pair or flush. Aside from that, it does not return the proper number. The strange thing is that everything else in the statement will run- if there is a three of a kind forced but not ordered, the hand will be reordered to be proper. The same occurs with a four of a kind. Comments are given where the value of BestCard is changed. int BestCard and String[] temp are already initialized.
well I found your error.
Lets say your hand is 3 2 K 3 Q
your i = 0 so n = 1 and p = 2
for the initial loop through nothing really happens,
so p goes through
and on the second go through, when p = 2. you hit a problem
the first line is
if(hand i == hand p)
at this point hand i = 3 AND hand p = 3
BUT you do not have 3 of a kind you simply have a pair.
Also the program is going to give this error again once the p for loop finishes and n=2 because then p will again start at 3 and return that you have 3 of a kind, when you really only have a pair.
I hope this helps.
I would suggest making each of them their own for loops instead of trying to do them all at once, a lot less confusing, and easier to do when you are newer to java.
PS you should probably change your bestcard values so you can win by high card if no one has a pair.

Dynamic programming with large inputs

I am trying to solve a classic Knapsack problem with huge capacity of 30.000.000 and it works well up until 20.000.000 but then it runs out of memory:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
I have tried to divide all values and capacity by 1.000.000 but that generates floats and I don't think that is the correct approach. I have also tried to make the arrays and matrix of type long but that does not help.
Perhaps another data-structure?
Any pointers welcome...
Code:
public class Knapsack {
public static void main(String[] args) {
int N = Integer.parseInt(args[0]); // number of items
int W = Integer.parseInt(args[1]); // maximum weight of knapsack
int[] profit = new int[N+1];
int[] weight = new int[N+1];
// generate random instance, items 1..N
for (int n = 1; n <= N; n++) {
profit[n] = (int) (Math.random() * 1000000);
weight[n] = (int) (Math.random() * W);
}
// opt[n][w] = max profit of packing items 1..n with weight limit w
// sol[n][w] = does opt solution to pack items 1..n with weight limit w include item n?
int[][] opt = new int[N+1][W+1];
boolean[][] sol = new boolean[N+1][W+1];
for (int n = 1; n <= N; n++) {
for (int w = 1; w <= W; w++) {
// don't take item n
int option1 = opt[n-1][w];
// take item n
int option2 = Integer.MIN_VALUE;
if (weight[n] <= w) option2 = profit[n] + opt[n-1][w-weight[n]];
// select better of two options
opt[n][w] = Math.max(option1, option2);
sol[n][w] = (option2 > option1);
}
}
// determine which items to take
boolean[] take = new boolean[N+1];
for (int n = N, w = W; n > 0; n--) {
if (sol[n][w]) { take[n] = true; w = w - weight[n]; }
else { take[n] = false; }
}
// print results
System.out.println("item" + "\t" + "profit" + "\t" + "weight" + "\t" + "take");
for (int n = 1; n <= N; n++) {
System.out.println(n + "\t" + profit[n] + "\t" + weight[n] + "\t" + take[n]);
}
//Copyright © 2000–2011, Robert Sedgewick and Kevin Wayne. Last updated: Wed Feb 9 //09:20:16 EST 2011.
}
Here are a couple of tricks I've used for things like that that.
First, a variant of a sparse matrix. It's not really sparse, but instead of assuming that "non-stored entries" are zero, you assume they're the same as the entry before. This can work in either direction (in the direction of the capacity or in the direction of the items), afaik not (easily) in both directions at the same time. Good trick, but doesn't defeat instances that are huge in both directions.
Secondly, a combination of Dynamic Programming and Branch & Bound. First, use DP with only the "last two rows". That gives you the value of the optimal solution. Then use Branch & Bound to find the subset of items that corresponds to the optimal solution. Sort by value/weight, apply the relaxation value[next_item] * (capacity_left / weight[next_item]) to bound with. Knowing the optimal value ahead of time makes pruning very effective.
The "last two rows" refers to the "previous row" (a slice of the tableau that has the solutions for all items up to i) and the "current row" (that you're filling right now). it could look something like this, for example: (this is C# btw, but should be easy to port)
int[] row0 = new int[capacity + 1], row1 = new int[capacity + 1];
for (int i = 0; i < weights.Length; i++)
{
for (int j = 0; j < row1.Length; j++)
{
int value_without_this_item = row1[j];
if (j >= weights[i])
row0[j] = Math.Max(value_without_this_item,
row1[j - weights[i]] + values[i]);
else
row0[j] = value_without_this_item;
}
// swap rows
int[] t = row1;
row1 = row0;
row0 = t;
}
int optimal_value = row1[capacity];
Use a recursive method to solve the problem. see http://penguin.ewu.edu/~trolfe/Knapsack01/Knapsack01.html for further information.
Hope it will be of help.
Break your for loops down into method calls.
This will have the effect of making the local variables GC'able once the method itself has completed.
So instead of nested for loops within the same main method call a method with the same functionality, which then calls a second method and you are effectively breaking the code up into small packets of local variables which can be collected when out of scope.

How to make n nested for loops recursively?

I have a method that must do the following:
for (int a01 = 1; a01 <= 25; a01++) {
for (int a02 = a01 + 1; a02 <= 25; a02++) {
for (int a03 = a02 + 1; a03 <= 25; a03++) {
...
System.out.println(a01 + "," + a02 + "," + ... + "," + a015);
}
}
}
I'd like to specify the number of nested for's (in the case above, I want 15 nested for's).
Is there a way to use recursive programming here?
Yes. This can be performed by recursive programming.
I assume you do not like to WRITE DOWN these nested for's in source code - as in your example, because this is really ugly programming - like the commentors explain.
The following (pseudo Java-like) code illustrates it. I assume a fixed depth for the nesting. Then you actually like to loop over an integer vector of dimension depth.
int[] length = new int[depth];
int[] counters = new int[depth];
The array counters has to be initialised to 0 (Arrays.fill(counters,0)). The array length has to be initialised to the number of iterations for the respective for loop.
I assume that you like to perform a certain operation within the inner loop. I will call this
performOperation(int[] counters);
- it depends on the multi-dimensional counter, i.e. the counters of the outer for's.
Then you can run the nested for loops by calling
nestedLoopOperation(counters, length, 0);
where
void nestedLoopOperation(int[] counters, int[] length, int level) {
if(level == counters.length) performOperation(counters);
else {
for (counters[level] = 0; counters[level] < length[level]; counters[level]++) {
nestedLoopOperation(counters, length, level + 1);
}
}
}
In your case your System.out.println() would be
performOperation(int[] counters) {
String counterAsString = "";
for (int level = 0; level < counters.length; level++) {
counterAsString = counterAsString + counters[level];
if (level < counters.length - 1) counterAsString = counterAsString + ",";
}
System.out.println(counterAsString);
}
I created this program to show all the different possible combination of cards (non repeating). It uses recursive for loops. Maybe it can help you.
//I'm lazy, so yeah, I made this import...
import static java.lang.System.out;
class ListCombinations {
//Array containing the values of the cards
static Symbol[] cardValues = Symbol.values();
//Array to represent the positions of the cards,
//they will hold different card values as the program executes
static Symbol[] positions = new Symbol[cardValues.length];
//A simple counter to show the number of combinations
static int counter = 1;
/*Names of cards to combine, add as many as you want, but be careful, we're
talking about factorials here, so 4 cards = 24 different combinations (4! = 24),
but 8 cards = 40320 combinations and 13 cards = 6.23 billion combinations!*/
enum Symbol {
AofSpades, TwoofSpades, ThreeofSpades, FourofSpades
}
public static void main(String args[]) {
//I send an argument of 0 because that is the first location which
//we want to add value to. Every recursive call will then add +1 to the argument.
combinations(0);
}
static void combinations(int locNumber) {
/* I use a recursive (repeating itself) method, since nesting for loops inside
* each other looks nasty and also requires one to know how many cards we will
* combine. I used 4 cards so we could nest 4 for loops one after another, but
* as I said, that's nasty programming. And if you add more cards, you would
* need to nest more for loops. Using a recursive method looks better, and gives
* you the freedom to combine as many cards as you want without changing code. */
//Recursive for loop nesting to iterate through all possible card combinations
for(int valueNumber = 0; valueNumber < cardValues.length; valueNumber++) {
positions[locNumber] = cardValues[valueNumber];
if (locNumber < (cardValues.length-1)) {
combinations(locNumber + 1);
}
//This if statement grabs and displays card combinations in which no card value
// is repeated in the current "positions" array. Since in a single deck,
// there are no repeated cards. It also appends the combination count at the end.
if (locNumber == (cardValues.length-1) && repeatedCards(positions)) {
for (int i = 0; i < cardValues.length; i++) {
out.print(positions[i]);
out.print(" ");
}
out.printf("%s", counter);
counter++;
out.println();
}
}
}
static boolean repeatedCards(Symbol[] cards) {
/*Method used to check if any cards are repeated in the current "positions" array*/
boolean booleanValue = true;
for(int i = 0; i < cardValues.length; i++) {
for(int j = 0; j < cardValues.length; j++) {
if(i != j && cards[i] == cards[j]) {
booleanValue = false;
}
}
}
return booleanValue;
}
}

Categories

Resources