Related
I am looking to create an algorithm in Java that can take any number of "players" and group them up a specified number of times each. However, two pairs cannot be the same. So, if we are supplied 9 players (dubbed 0, 1, 2, etc) by the user and each player should be paired 3 times, that means that this algorithm needs to be able to generate a list of pairs where each player is paired 3 times.
So 4 players being paired two times could be: {{0, 1}, {2, 3}, {0, 2}, {1, 3}}.
Obviously, it can be impossible in some scenarios (like 4 players being uniquely paired 20 times), but I have input restrictions to combat that.
{0, 1} and {1, 0} are equal pairs. The order of the numbers does not matter, they are not unique.
The preferable way for input is just given two numbers (number of players, number of pairs per players) and the preferable way for the output to be given is in a two dimensional array of integers (each player being dubbed by an integer), like I gave an example of.
Does anyone have any ideas on how to do this? Pseudo-code, actual code, any ideas are welcome. Thanks!
I think your question is valid and interesting to solve in code.
This is why I coded that whole thing.
There's one downside to my solution, or rather: the problem.
In certain situations, some players can have many matches between them, while others have little. So in the end, some players might not get matched properly.
In this case, you'd need a mathematical trick, or a backtracking algorithm, that steps back on parts of the solution and tries (brute-forces) other combinations. My algorithm has neither, but it indicates Exceptions and validity.
Also check the comments in the code.
package snippet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
public class BadPairingStuff6 {
static class Player {
public final int mID;
private final BadPairingStuff6 mParentLogic;
public int mMatches;
public Player(final int pID, final BadPairingStuff6 pBadPairingStuff5) {
mID = pID;
mParentLogic = pBadPairingStuff5;
}
#Override public int hashCode() {
return mID;
}
#Override public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Player other = (Player) obj;
if (mID != other.mID) return false;
return true;
}
#Override public String toString() {
return "Player[" + mID + "]";
}
public void incMatches() {
++mMatches;
}
public int getMatches() {
return mMatches;
}
public boolean canPlayAnotherMatch() {
return getMatches() < mParentLogic.mPairingsAllowed;
}
}
static class Pairing {
public final Player mPlayer1;
public final Player mPlayer2;
public Pairing(final Player pPlayer1, final Player pPlayer2) {
if (pPlayer1.mID < pPlayer2.mID) {
mPlayer1 = pPlayer1;
mPlayer2 = pPlayer2;
} else {
mPlayer1 = pPlayer2;
mPlayer2 = pPlayer1;
}
}
#Override public String toString() {
return mPlayer1 + "+" + mPlayer2;
}
#Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mPlayer1.mID;
result = prime * result + mPlayer2.mID;
return result;
}
#Override public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Pairing other = (Pairing) obj;
if (mPlayer1 != other.mPlayer1) return false;
if (mPlayer2 != other.mPlayer2) return false;
return true;
}
}
static class PartnerMap {
private final HashMap<Player, ArrayList<Player>> mMap = new HashMap<>();
public PartnerMap(final Iterable<Player> pPlayers) {
for (final Player player : pPlayers) {
final ArrayList<Player> partners = new ArrayList<>();
for (final Player partner : pPlayers) {
if (player != partner) partners.add(partner);
}
mMap.put(player, partners);
}
}
public Player getPartner(final Player pPlayer) {
final ArrayList<Player> possiblePartners = mMap.get(pPlayer);
if (possiblePartners.size() < 1) throw new NotEnoughPartnersException(pPlayer);
return possiblePartners.get((int) (Math.random() * possiblePartners.size()));
}
public void removePartners(final Player pPlayer, final Player pPartner) {
System.out.println("\t\tBadPairingStuff5.PartnerMap.removePartners(" + pPlayer + ", " + pPartner + ")");
System.out.println("\t\t\tRemoving for " + pPlayer);
System.out.println("\t\t\t\tBEFORE: " + toString(mMap.get(pPlayer)));
mMap.get(pPlayer).remove(pPartner);
System.out.println("\t\t\t\tAFTER: " + toString(mMap.get(pPlayer)));
System.out.println("\t\t\tRemoving for " + pPartner);
System.out.println("\t\t\t\tBEFORE: " + toString(mMap.get(pPartner)));
mMap.get(pPartner).remove(pPlayer);
System.out.println("\t\t\t\tAFTER: " + toString(mMap.get(pPartner)));
}
static String toString(final Iterable<Player> pPlayers) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
for (final Player player : pPlayers) {
sb.append(player.mID + " ");
}
sb.append("]");
return sb.toString();
}
public void removePlayerCompletely(final Player pPlayer) {
System.out.println("\t\t\tBadPairingStuff5.PartnerMap.removePlayerCompletely(" + pPlayer + ")");
for (final ArrayList<Player> partnerMap : mMap.values()) {
partnerMap.remove(pPlayer);
}
mMap.get(pPlayer).clear();
}
public void print() {
System.out.println("Partner Map");
for (final Entry<Player, ArrayList<Player>> e : mMap.entrySet()) {
System.out.println("\t" + e.getKey());
for (final Player v : e.getValue()) {
System.out.println("\t\t" + v);
}
}
}
}
public static class NotEnoughPartnersException extends IllegalStateException {
private static final long serialVersionUID = -7249807214069096317L;
private final Player mPlayer;
public NotEnoughPartnersException(final Player pPlayer) {
super("Not enough partners available for " + pPlayer + "!");
mPlayer = pPlayer;
}
public Player getPlayer() {
return mPlayer;
}
}
static class PairingResult {
public final ArrayList<Pairing> mCreatedPairings;
public final ArrayList<Exception> mExceptions;
public PairingResult(final ArrayList<Pairing> pCreatedPairings, final ArrayList<Exception> pExceptions) {
mCreatedPairings = pCreatedPairings;
mExceptions = pExceptions;
}
public boolean isValid() {
return mExceptions.size() < 1;
}
}
public static void main(final String[] args) {
final int players = 10;
final int pairingsAllowed = 4;
final PairingResult result = new BadPairingStuff6(players, pairingsAllowed).createPairings();
System.out.println("All pairings:");
final HashMap<Long, Long> playCounter = new HashMap<>();
for (final Pairing p : result.mCreatedPairings) {
System.out.println("\t" + p);
{
final Long oldCount = playCounter.get(Long.valueOf(p.mPlayer1.mID));
playCounter.put(Long.valueOf(p.mPlayer1.mID), Long.valueOf(oldCount == null ? 1 : (oldCount.longValue() + 1)));
}
{
final Long oldCount = playCounter.get(Long.valueOf(p.mPlayer2.mID));
playCounter.put(Long.valueOf(p.mPlayer2.mID), Long.valueOf(oldCount == null ? 1 : (oldCount.longValue() + 1)));
}
}
System.out.println("Pairings per Player: ");
for (final Entry<Long, Long> e : playCounter.entrySet()) {
System.out.println("\t" + e.getKey() + " -> " + e.getValue());
}
System.out.println("Exceptions:");
System.out.flush();
sleep();
for (final Exception e : result.mExceptions) {
e.printStackTrace();
}
System.err.flush();
sleep();
System.out.println("Valid result: " + result.isValid());
System.out.println("All done.");
}
/*
* OBJECT
*/
final int mPairingsAllowed;
private final ArrayList<Player> mPlayers = new ArrayList<>();
public BadPairingStuff6(final int pPlayersCount, final int pPairingsAllowed) {
mPairingsAllowed = pPairingsAllowed;
// create players
for (int i = 0; i < pPlayersCount; i++) {
mPlayers.add(new Player(i, this));
}
}
public PairingResult createPairings() {
final ArrayList<Pairing> createdPairings = new ArrayList<>();
final ArrayList<Exception> exceptions = new ArrayList<>();
final PartnerMap possiblePairings = new PartnerMap(mPlayers);
final HashSet<Player> playersToHandle = new HashSet<>(mPlayers);
while (!playersToHandle.isEmpty()) {
final ArrayList<Player> removePlayersPerRound = new ArrayList<>();
for (final Player player : playersToHandle) {
if (!player.canPlayAnotherMatch()) {
possiblePairings.removePlayerCompletely(player);
removePlayersPerRound.add(player);
continue;
}
try {
System.out.println("Creating matches for " + player + " (" + player.getMatches() + ")");
final Player partner = possiblePairings.getPartner(player);
if (!partner.canPlayAnotherMatch()) continue;
final Pairing newPairing = new Pairing(player, partner);
if (createdPairings.contains(newPairing)) System.out.println("WARNING! Double hit for " + newPairing);
createdPairings.add(newPairing);
possiblePairings.removePartners(player, partner);
player.incMatches();
partner.incMatches();
System.out.println("\tMatched with " + partner);
if (!partner.canPlayAnotherMatch()) {
possiblePairings.removePlayerCompletely(partner);
removePlayersPerRound.add(partner);
}
} catch (final NotEnoughPartnersException e) {
// the flushes and sleeps are only a cheap shot to keep System.out and System.err outputs in somewhat chronological order.
// this is for proof/debug/answer only, and should NOT be used in production!
System.out.flush();
sleep();
e.printStackTrace();
// throw e; // if you want to abort early
removePlayersPerRound.add(e.getPlayer());
exceptions.add(e);
System.err.flush();
sleep();
}
}
playersToHandle.removeAll(removePlayersPerRound);
}
possiblePairings.print();
return new PairingResult(createdPairings, exceptions);
}
// the sleeps are only a cheap shot to keep System.out and System.err outputs in somewhat chronological order.
// this is for proof/answer only, and should NOT be used in production
static void sleep(final long pMilliSec) {
try {
Thread.sleep(pMilliSec);
} catch (final InterruptedException e1) { /* */ }
}
static void sleep() {
sleep(100);
}
}
I use lots of inner static classes. This is for demonstration purposes only.
If you want to actually use those classes, put each of them into its separate file (remove the "static class", and add a "public class" where it's missing).
Also note that this complexity is needed for random assignments. If the algorithm could always churn out the same combinations, it would be about 1/10th of the code.
I am trying to create a program that when given the location of a chess knight and the destination, all marked in chess notation, to return the number of moves it takes the knight to get from the location the destination. I have tried before using the algorithm to calculate every single possibility on a list, but it is very slow and kind of has problems. Here is my code:
private static int translateChessNotation(String chess) {
int returned = 8 * (Integer.valueOf(String.valueOf(chess.charAt(1)))- 1);
return returned + (convertAlphabet(chess.charAt(0))); // File
}
public static int knight(String start, String finish) {
int knightPosition = translateChessNotation(start), end = translateChessNotation(finish), i = 0;
ArrayList<Integer> currentPossibleKnightPositions = new ArrayList<>();
currentPossibleKnightPositions.add(knightPosition);
for (; i < 8; i++) {
ArrayList<Integer> newList = new ArrayList<>();
for (int position : currentPossibleKnightPositions) {
newList.add(position + 17);
newList.add(position + 15);
newList.add(position + 10);
newList.add(position + 6);
newList.add(position - 6);
newList.add(position - 10);
newList.add(position - 15);
newList.add(position - 17);
}
ArrayList<Integer> removed = new ArrayList<>();
for (int j : newList) {if (j < 1 || j > 64) {removed.add(j);}}
newList.removeAll(removed);
currentPossibleKnightPositions.clear();
currentPossibleKnightPositions.addAll(newList);
for (int n : currentPossibleKnightPositions) {
if (n == end) {return i + 1;}
}
}
return -1;
}
Thanks a lot if you help!
Here's a little Proggy to solve the so-called Knights-Tour problem, visiting all squares on the board starting from a particular location, so you could adapt that to set a particular to-position as your end-condition.
Its just Brute-Force, trying all possible combinations & takes about 50 minutes to find each full Knights-Tour solution.
If that helps, I'd be honoured to receive your vote.
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class KnightMove {
#SuppressWarnings("serial")
private static final class KnightMoveSolvedException extends RuntimeException {
private final byte[][] solution;
private KnightMoveSolvedException(final byte[][] solution) {
this.solution = solution;
}
}
private static final int SIZE_X = 8;
private static final int SIZE_Y = 8;
private static final int SIZE_X_Y = SIZE_X * SIZE_Y; // Max 127! (=0x7F)
private static final int [][] KNIGHT_MOVES = new int[8][];
/**/ static {
final AtomicInteger moveIndex = new AtomicInteger();
IntStream.of(2, -2).forEach(deltaX ->
IntStream.of(1, -1).forEach(deltaY -> {
/*
* Mirror the 4 combinations above to get all 8 possible Knight moves...
*/
KNIGHT_MOVES[moveIndex.getAndIncrement()] = new int[] {deltaX, deltaY};
KNIGHT_MOVES[moveIndex.getAndIncrement()] = new int[] {deltaY, deltaX};
}));
}
private static void nextMoveToXY(int moveCount, final int x, final int y, final byte[][] board) {
moveCount++;
board[x][y] = (byte) moveCount;
if (moveCount >= SIZE_X_Y) {
System.out.println("Solved!.....: count=" + moveCount);
for ( final byte[] column : board ) {
for (final byte square : column) {
System.out.print(square + "\t");
}
System.out.println();
}
return; // (Back up & keep looking for next solution)
/*
* If 1 solution is enough, just throw the Exception...
*/
// throw new KnightMoveSolvedException(board);
}
for (final int[] knightMove : KNIGHT_MOVES) {
final int newX = x + knightMove[0]; if (newX < 0 || newX >= SIZE_X) {continue;}
final int newY = y + knightMove[1]; if (newY < 0 || newY >= SIZE_Y) {continue;}
if (board[newX][newY] == 0) {
/*
* Target Square is vacant, so try this move recursively...
*/
nextMoveToXY(moveCount, newX, newY, deepPrimitive2DArrayClone(board));
}
}
}
/**
* {#link Object#clone()} can create a Deep Clone of a 1D array of Primitives
* but will <b>not</b> deliver the desired result with 2D,
* so we have to wrap the rest by hand...
*/
private static byte[][] deepPrimitive2DArrayClone(final byte[][] source) {
final byte[][] clone = new byte[source.length][];
/**/ int cix = 0;
for (final byte[] col : source) {
clone[cix++] = col.clone();
}
return clone;
}
public static void main(final String[] args) throws Exception {
IntStream.range(0, SIZE_X).forEach(x ->
IntStream.range(0, SIZE_Y).forEach(y -> {
try {
System.out.println("Solve starting at X/Y.: " + x +"/" + y);
nextMoveToXY(0, x, y, new byte[SIZE_X][SIZE_Y]);
}
catch (final KnightMoveSolvedException e) {
System.out.println(e.solution);
}
}));
}
}
I got this answer online. Hope this helps to the others who have the same question!
public static int knight(String...pos) {
int[][] ab=Stream.of(pos).map(s->new int[]{"abcdefgh".indexOf(s.charAt(0)),s.charAt(1)-48}).toArray(int[][]::new);
int[] dxy=IntStream.range(0,2).map(i->Math.abs(ab[0][i]-ab[1][i])).sorted().toArray();
if(dxy[0]==0&&dxy[1]==1) return 3;
if(dxy[0]==2&&dxy[1]==2||dxy[0]==1&&dxy[1]==1&&(pos[0]+pos[1]).matches(".*?(a1|h1|a8|h8).*")) return 4;
int delta=dxy[1]-dxy[0];
return delta-2*(int)Math.floor(1.0*(delta-dxy[0])/(dxy[0]>delta?3:4));
}
I've replaced the Chessboard Array in the previous Posting with a long.
(with 64 bits, its just large enough to represent the board)
The new Version is significantly faster.
Depending on starting-coordinates, a solution takes anywhere between 1 Minute & 12 Hours...
(I've put a couple of the faster ones first)
This example is designed to show the basics. There are various Mathematical Methods (see Wikipedia) to optimise it, but they make the Solution more complex.
A couple of Takeaways:
- use Primitives (byte, short, int, long,...) if you can: they are very fast
- avoid Objects like ArrayList when using Brute-Force: they are very slow
- use recursion: it saves & restores State for you. It may cost a little, but it makes life so much easier
- use final whenever you can: it's no faster, but aids understanding
Hope you like it. :-)
I've honed this thing down now. It is massively faster than the original (which was no slouch!), uses the Warnsdorff algorithm & can solve multiple starting positions, running on all available Threads simultaneously.
Most of the work is getting the Data Structures right & Initialisation.
The recursive nextMoveToXY solver Method itself is trivially simple.
The Warnsdorff Version:
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
public class KnightsTourWarnsdorff {
private interface IntIntConsumer {
void accept(int t, int u);
}
private static final int MAX_INSTANT_TO_STRING_LENGTH = "2020-12-31T23:59:59.123456Z".length();
private static final int SIZE_X = 8;
private static final int SIZE_Y = 8;
private static final int SIZE_X_Y = SIZE_X * SIZE_Y;
/**/ static { check_SIZE_X_Y_withinCapacity();}
/**
* Do this in a Method (we don't want to mark the Class with SuppressWarnings)
*/
#SuppressWarnings("unused")
private static void check_SIZE_X_Y_withinCapacity() {
if (SIZE_X_Y > Long.SIZE) {
throw new UnsupportedOperationException("Number of squares on board exceeds capacity of long Solution");
}
}
/**
* Returns the unique offset corresponding to a Move or our position on the Board.
*/
private static int getDeltaXY(final int deltaX, final int deltaY) {
return deltaX + deltaY * SIZE_X; /* Yes, SIZE_X ! */
}
/**
* Returns a long with a single bit set, corresponding to our position on the Board.
*/
private static long getXYmaskBit(final int x, final int y) {
return 1L << (63 - getDeltaXY(x, y));
}
private static void walkBoard(final IntIntConsumer doXY) {
walkBoard(null, doXY, null);
}
private static void walkBoard(final IntConsumer doRowStart, final IntIntConsumer doXY, final Runnable doRowEnd) {
IntStream .range(0, SIZE_Y).forEach(y -> {if (doRowStart != null) {doRowStart.accept( y);}
IntStream.range(0, SIZE_X).forEach(x -> {if (doXY != null) {doXY .accept(x,y);}
}); if (doRowEnd != null) {doRowEnd .run ( );}
});
}
private static String toBinary(final long value) {
return leftPad(Long.SIZE, Long.toBinaryString(value)).replace('0', '_');
}
private static String leftPad (final int paddedLength, final String value) {
final int padCount = Math.max(0, paddedLength - value.length());
final char[] pad = new char[padCount];
Arrays.fill (pad, '0');
return String.valueOf(pad).concat(value);
}
private static String rightPad (final int paddedLength, final String value) {
final int padCount = Math.max(0, paddedLength - value.length());
final char[] pad = new char[padCount];
Arrays.fill (pad, '0');
return value.concat(String.valueOf(pad));
}
private static String header () {
return rightPad (MAX_INSTANT_TO_STRING_LENGTH, Instant.now().toString()) + " " + Thread.currentThread().getName() + " ";
}
/**
* Square on Board not only knows its x/y location, but also its position as an xyMask<br>
* (for checking whether a square is occupied & marking as occupied).<br>
* <br>
* It knows all possible Moves from this Square within the Board<br>
* (thus obviating the need to check whether we're still on the Board).<br>
* <br>
* Each Specific Move contains a reference to the Target Square, which in turn...<br>
* (these 2 measures speed up Navigation massively)
*/
private static final class Square {
private final int x;
private final int y;
/**
* Used to mark the Square as occupied on the Board
*/
private final long xyMask;
/**
* All possible Moves from this Square.<br>
* (initially all null: filled after all Squares have been instantiated)
*/
private final Move[] targetMove;
private Square(final int x, final int y) {
this.x = x;
this. y = y;
this.xyMask = getXYmaskBit(x, y);
this.targetMove = KNIGHT_MOVE_MAP.values().stream().filter(move -> {
final int newX = x + move.deltaX;
final int newY = y + move.deltaY;
return newX >= 0 && newX < SIZE_X
&& newY >= 0 && newY < SIZE_Y;
}).toArray(Move[]::new);
}
}
/**
* Either a Generic or a Specific Move
*/
private static final class Move {
private final int deltaX;
private final int deltaY;
private final int deltaXY;
private final Square target;
/**
* Create a Generic Move
*/
private Move(final int deltaX, final int deltaY) {
this.deltaX = deltaX;
this.deltaY = deltaY;
this.deltaXY = getDeltaXY(deltaX, deltaY);
this.target = null;
}
/**
* Create a Move to a specific Target Square
*/
private Move(final Move genericMove, final Square target) {
this.deltaX = genericMove.deltaX;
this.deltaY = genericMove.deltaY;
this.deltaXY = genericMove.deltaXY;
this.target = target;
}
}
#SuppressWarnings("serial")
private static final class KnightMoveSolvedException extends RuntimeException {
private final int[] solution;
private KnightMoveSolvedException(final int moveCount, final int[] solution) {
/*
* Trim the solution array down to the number of moves...
* (for those performing a partial walk)
*/
this.solution = Arrays.stream(solution).limit(moveCount).toArray();
synchronized (KnightMoveSolvedException.class) { // One Thread (= Solution) at a time please!
final int solution0 = this.solution[0];
final Move initialMove = BOARD_MAP.get(solution0);
final int initialX = initialMove.deltaX;
final int initialY = initialMove.deltaY;
System.out.println(header() + "Solution found for....: x/y: " + initialX + "/" + initialY + " \t" + toBinary(0L) + " \tlength=" + this.solution.length + " \t" + solution0);
this.printSolutionDetail();
}
}
private void printSolutionDetail() {
int x = 0;
int y = 0;
long board = 0;
for (int i=0; i < this.solution.length; i++) {
final int positionOrMove = this.solution[i];
final Move move = i == 0 ? BOARD_MAP.get(positionOrMove) : KNIGHT_MOVE_MAP.get(positionOrMove);
/**/ x = i == 0 ? move.deltaX : x + move.deltaX;
/**/ y = i == 0 ? move.deltaY : y + move.deltaY;
board |= getXYmaskBit(x, y);
System.out.println(header() + "Solution walk.........: x/y: " + x + "/" + y + " \t" + toBinary(board) + " \t" + move.deltaX + "\t" + move.deltaY + "\t" + positionOrMove);
}
}
}
private static final Map<Integer, Move> KNIGHT_MOVE_MAP;
/**/ static {
final Map<Integer, Move> Knight_Move_Map = new TreeMap<>();
IntStream.of(2, -2).forEach(deltaX ->
IntStream.of(1, -1).forEach(deltaY -> {
/*
* Mirror the 4 combinations above to get all 8 possible Knight moves...
*/
{final Move move = new Move(deltaX, deltaY); Knight_Move_Map.put(move.deltaXY, move);}
{final Move move = new Move(deltaY, deltaX); Knight_Move_Map.put(move.deltaXY, move);}
}));
KNIGHT_MOVE_MAP = Collections.unmodifiableMap(Knight_Move_Map);
}
private static final Map<Integer, Move> BOARD_MAP;
/**/ static {
final Map<Integer, Move> Board_Map = new TreeMap<>();
walkBoard((x,y) -> {
final Move move = new Move(x, y);
Board_Map.put(move.deltaXY, move);
});
BOARD_MAP = Collections.unmodifiableMap(Board_Map);
}
private static final Square[][] BOARD = new Square[SIZE_X] [SIZE_Y];
/**/ static {
/*
* Fill the Board with Squares...
*/
walkBoard( (x,y) -> BOARD[x][y] = new Square(x, y));
/**/ System.out.println("Onward Target Count:");
walkBoard( ( y) -> { System.out.print ( y + " : ");},
/**/ (x,y) -> {final Square square = BOARD[x][y]; System.out.print (square.targetMove.length + " ");},
/**/ ( ) -> { System.out.println() ;} );
/*
* So far the Target Moves array is filled with nulls. We MUST fill it...
*/
Arrays.stream(BOARD).flatMap(Arrays::stream).forEach(square -> {
final Move[] targetsSortedByOnwardPointCount = Arrays
.stream(square.targetMove)
.sorted((moveA, moveB) -> {
/*
* We use the Warnsdorff algorithm to sort it by the number of Onward Targets...
*/
final Square targetA = BOARD[square.x + moveA.deltaX] [square.y + moveA.deltaY];
final Square targetB = BOARD[square.x + moveB.deltaX] [square.y + moveB.deltaY];
return Integer.compare(
targetA.targetMove.length, // number of Onward Targets
targetB.targetMove.length); // number of Onward Targets
})
.map(move -> new Move(move, BOARD[square.x + move.deltaX] [square.y + move.deltaY]))
.toArray(Move[]::new);
/*
* Original & sorted arrays should be the same length if we got it right,
* so take max. length as a precaution to force an IndexOutOfBoundsException if we didn't...
*/
final int copyLength = Math.max(square.targetMove.length, targetsSortedByOnwardPointCount.length);
/*
* Overwrite the original Moves with the sorted version...
*/
System.arraycopy(targetsSortedByOnwardPointCount, 0, square.targetMove, 0, copyLength);
});
}
private final int[] SOLUTION = new int[SIZE_X_Y];
private void solve(final int initialX, final int initialY) {
final long initialBoard = getXYmaskBit(initialX, initialY);
System.out.println(header() + "Solve starting at.....: x/y: " + initialX +"/" + initialY + "\t" + toBinary(initialBoard));
try {
SOLUTION [0] = getDeltaXY(initialX, initialY); // First Entry contains Starting-Point
nextMoveToXY(0, BOARD[initialX][initialY], initialBoard);
}
catch (final KnightMoveSolvedException justIgnore_WereDone) {}
}
private void nextMoveToXY(int moveCount, final Square square, final long board) {
moveCount++;
if (moveCount >= SIZE_X_Y) {
final KnightMoveSolvedException solution = new KnightMoveSolvedException(moveCount, SOLUTION);
// return; // (Back up & keep looking for next solution)
/*
* If 1 solution is enough, just throw the Exception...
*/
throw solution;
}
for (final Move move : square.targetMove) {
/*
* Is Target Square vacant? (i.e. Mask Bit not set)...
*/
if ((board & move.target.xyMask) == 0) {
/*
* Yes: try next move recursively with new Position & Board...
*/
SOLUTION [moveCount] = move.deltaXY;
nextMoveToXY(moveCount, move.target, board | move.target.xyMask /* Set Mask Bit on new Board */);
}
}
}
public static void main(final String[] args) throws Exception {
final ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/*
* We can handle rectangular boards, but for square boards the following holds:
* we only need to solve for 1/8 of the board (a triangle)...
* (the remaining 7/8 are either Mirrors or Rotations of the 1/8)
*/
IntStream .range(0, SIZE_X / 2).forEach(x -> {
IntStream.range(0, x + 1 ).forEach(y -> {
pool.submit(() -> {
try { TimeUnit.SECONDS.sleep(1); } catch (final InterruptedException e) {}
/*
* (Sleep very briefly, so our Thread won't start before the Output below has finished)
*/
new KnightsTourWarnsdorff().solve(x, y);
});
System.out.print("x=" + x + " y=" + y + "\t");
});
System.out.println();
});
pool.shutdown();
}
}
Original Version:
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class KnightsTour {
#SuppressWarnings("serial")
private static final class KnightMoveSolvedException extends RuntimeException {
private final int[][] solution;
private KnightMoveSolvedException(final int[][] solution) {
this.solution = deepPrimitive2DArrayClone (solution);
}
}
private static final int SIZE_X = 8;
private static final int SIZE_Y = 8;
private static final int SIZE_X_Y = SIZE_X * SIZE_Y;
private static final int[][] SOLUTION = new int[SIZE_X_Y][];
private static final int INDEX_X = 0;
private static final int INDEX_Y = 1;
private static final int KNIGHT_MOVES_LENGTH = 8;
private static final int [][] KNIGHT_MOVES = new int[KNIGHT_MOVES_LENGTH][];
/**/ static {
checkLongSolutionCapacity();
final AtomicInteger moveIndex = new AtomicInteger();
IntStream.of(2, -2).forEach(deltaX ->
IntStream.of(1, -1).forEach(deltaY -> {
/*
* Mirror the 4 combinations above to get all 8 possible Knight moves...
*/
KNIGHT_MOVES[moveIndex.getAndIncrement()] = new int[] {deltaX, deltaY};
KNIGHT_MOVES[moveIndex.getAndIncrement()] = new int[] {deltaY, deltaX};
}));
}
#SuppressWarnings("unused")
private static void checkLongSolutionCapacity() {
if (SIZE_X_Y > Long.SIZE) {
throw new UnsupportedOperationException("Number of squares on board exceeds capacity of long Solution");
}
}
private static long getXYmaskBit(final int x, final int y) {
return Long.MIN_VALUE >>> (x + y * SIZE_X /* Yes, SIZE-X ! */);
}
public static void solve(final int initialX, final int initialY) {
final long initialBoard = getXYmaskBit(initialX, initialY);
System.out.println("Solve starting at X/Y.: " + initialX +"/" + initialY + "\t" + toBinary(initialBoard));
try {
SOLUTION [0] = new int[] {initialX, initialY}; // First Entry contains Starting-Point
nextMoveToXY(0, initialX, initialY, initialBoard);
}
catch (final KnightMoveSolvedException e) {
System.out.println("One possible solution.: " + e.solution);
}
}
private static void nextMoveToXY(int moveCount, final int x, final int y, final long board) {
moveCount++;
if (moveCount >= SIZE_X_Y) {
System.out.println("Solved!...............: count=" + moveCount);
/*
* Print the answer or remember it somewhere...
*/
final int initialX = SOLUTION[0][INDEX_X];
final int initialY = SOLUTION[0][INDEX_Y];
for(final int[] move : SOLUTION) {
final int solutionX = move[INDEX_X];
final int solutionY = move[INDEX_Y];
System.out.println("Move (starting at X/Y): " + initialX +"/" + initialY + "\t" + toBinary(board) + "\t" + solutionX + "\t" + solutionY);
}
// return; // (Back up & keep looking for next solution)
/*
* If 1 solution is enough, just throw the Exception...
*/
throw new KnightMoveSolvedException(SOLUTION);
}
for(final int[] move : KNIGHT_MOVES) {
final int deltaX = move[INDEX_X]; final int newX = x + deltaX; if (newX < 0 || newX >= SIZE_X) {continue;}
final int deltaY = move[INDEX_Y]; final int newY = y + deltaY; if (newY < 0 || newY >= SIZE_Y) {continue;}
/*
* ok: Checks above mean we're on the board, so lets find the new Position Mask...
*/
final long newXYmaskBit = getXYmaskBit(newX, newY);
/*
* Is Target Square vacant (= Mask Bit not set)?...
*/
if ((board & newXYmaskBit) == 0) {
/*
* Yes: try next move recursively with new Position & Board...
*/
SOLUTION [moveCount] = move;
nextMoveToXY(moveCount, newX, newY, board | newXYmaskBit /* Set Mask Bit on new Board */);
}
}
}
public static String toHex (final int value) {
return leftPad(Integer.BYTES * 2, Integer.toHexString (value));
}
public static String toHex (final long value) {
return leftPad(Long .BYTES * 2, Long .toHexString (value));
}
public static String toBinary(final int value) {
return leftPad(Integer.SIZE, Integer.toBinaryString(value));
}
public static String toBinary(final long value) {
return leftPad(Long .SIZE, Long .toBinaryString(value));
}
private static String leftPad (final int paddedLength, final String binaryOrHex) {
final char[] lead = new char[paddedLength - binaryOrHex.length()];
Arrays.fill (lead, '0');
return String.valueOf(lead).concat(binaryOrHex).replace('0', '_');
}
/**
* {#link Object#clone()} can create a Deep Clone of a 1D array of Primitives
* but with 2D will only provide a Shallow Copy (meaning if the content of source
* changes, the content of clone will change!!) so we have to wrap 2D by hand...
*/
private static int[][] deepPrimitive2DArrayClone(final int[][] source) {
final int[][] clone = new int[source.length][];
/**/ int cix = 0;
for (final int[] col : source) {
clone[cix++] = col.clone(); // (ok: 1D, so Deep Clone)
}
return clone;
}
public static void main(final String[] args) throws Exception {
solve(0, 1); // Fast!: 2 Minutes
solve(0, 3); // Fast!: 1 Minute
IntStream.range(0, SIZE_X).forEach(x ->
IntStream.range(0, SIZE_Y).forEach(y -> {
solve(x, y);
}));
}
}
I am currently making a hypothetical producer consumer problem using java. The object is to have an operating system which is 1000 bytes, but only 500 bytes available to use for threads as 500 bytes have already been consumed by drivers and other operations. The threads are as follows:
A thread to start a BubbleWitch2 session of 10 seconds, which requires 100 bytes of RAM per
second
A thread to start a Spotify stream of 20 seconds, which requires 250 bytes of RAM per second
You should also take into account the fact that the operating system is simultaneously supporting system
activity and managing the processor, memory and disk space of the device on which it is installed.
Therefore, additionally create:
System and management threads, which, together, require 50 bytes of RAM per second, and
execute for a random length of time, once invoked.
A thread to install a new security update of 2 KB, which will be stored to disk, and requires 150
bytes of RAM per second while installing. Assume sufficient disk capacity in the system to support
this thread.
The operating system has only capacity for 200 bytes per second, therefore a larger thread such as spotify will experience delays or be forced to wait. I have used code which as far as I can tell, implements this. I am also required to generate exit times which I have done with timestamps and to calculate average waiting times for threads.
I have included code in my solution for the average waiting times with system.out.print but no matter what I do, it is not actually outputting the times at all-as if they did not exist.
I am also not sure if the buffer size limitations are working as it is a matter of milliseconds-is there any way to tell if this is working from the code below?
My main method.
public class ProducerConsumerTest {
public static void main(String[] args) throws InterruptedException {
Buffer c = new Buffer();
BubbleWitch2 p1 = new BubbleWitch2(c,1);
Processor c1 = new Processor(c, 1);
Spotify p2 = new Spotify(c, 2);
SystemManagement p3 = new SystemManagement(c, 3);
SecurityUpdate p4 = new SecurityUpdate(c, 4, p1, p2, p3);
p1.setName("BubbleWitch2 ");
p2.setName("Spotify ");
p3.setName("System Management ");
p4.setName("Security Update ");
p1.setPriority(10);
p2.setPriority(10);
p3.setPriority(10);
p4.setPriority(5);
c1.start();
p1.start();
p2.start();
p3.start();
p4.start();
p2.join();
p3.join();
p4.join();
System.exit(0);
}
}
My buffer class
import java.text.DateFormat;
import java.text.SimpleDateFormat;
/**
* Created by Rory on 10/08/2014.
*/
class Buffer {
private int contents, count = 0, process = 0;
private boolean available = false;
private long start, end, wait, request= 0;
private DateFormat time = new SimpleDateFormat("mm:ss:SSS");
public synchronized int get() {
while (process <= 500) {
try {
wait();
} catch (InterruptedException e) {
}
}
process -= 200;
System.out.println("CPU After Process " + process);
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (process >= 1000) {
start = System.currentTimeMillis();
try {
wait();
} catch (InterruptedException e) {
}
end = System.currentTimeMillis();
wait = end - start;
count++;
request += wait;
System.out.println("Application Request Wait Time: " + time.format(wait));
process += value;
contents = value;
notifyAll();
}
}
}
My security update class
import java.lang.*;
import java.lang.System;
/**
* Created by Rory on 11/08/2014.
*/
class SecurityUpdate extends Thread {
private Buffer buffer;
private int number;
private int bytes = 150;
private int process = 0;
public SecurityUpdate(Buffer c, int number, BubbleWitch2 bubbleWitch2, Spotify spotify, SystemManagement systemManagement) throws InterruptedException {
buffer = c;
this.number = number;
bubbleWitch2.join();
spotify.join();
systemManagement.join();
}
public void run() {
for (int i = 0; i < 15; i++) {
buffer.put(i);
System.out.println(getName() + this.number
+ " put: " + i);
try {
sleep(1500);
} catch (InterruptedException e) {
}
}
System.out.println("-----------------------------");
System.out.println("Security Update has finished executing.");
System.out.println("------------------------------");
}
}
My processor class
class Processor extends Thread {
private Buffer processor;
private int number;
public Processor(Buffer c, int number) {
processor = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 60; i++) {
value = processor.get();
System.out.println("Processor #"
+ this.number
+ " got: " + value);
}
}
}
My bubblewitch class
import java.lang.*;
import java.lang.System;
import java.sql.Timestamp;
/**
* Created by Rory on 10/08/2014.
*/
class BubbleWitch2 extends Thread {
private Buffer buffer;
private int number;
private int bytes = 100;
private int duration;
public BubbleWitch2(Buffer c, int pduration) {
buffer = c;
duration = pduration;
}
long startTime = System.currentTimeMillis();
public void run() {
for (int i = 0; i < 10; i++) {
buffer.put(bytes);
System.out.println(getName() + this.number
+ " put: " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
}
}
long endTime = System.currentTimeMillis();
long timeTaken = endTime - startTime;
java.util.Date date = new java.util.Date();
System.out.println("-----------------------------");
System.out.println("BubbleWitch2 has finished executing.");
System.out.println("Time taken to execute was " +timeTaken+ " milliseconds");
System.out.println("Time Bubblewitch2 thread exited Processor was " + new Timestamp(date.getTime()));
System.out.println("-----------------------------");
}
}
My system management
class SystemManagement extends Thread {
private Buffer buffer;
private int number, min = 1, max = 15;
private int loopCount = (int) (Math.random() * (max - min));
private int bytes = 50;
private int process = 0;
public SystemManagement(Buffer c, int number) {
buffer = c;
this.number = number;
}
public void run() {
for (int i = 0; i < loopCount; i++) {
buffer.put(50);
System.out.println(getName() + this.number
+ " put: " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
}
}
System.out.println("-----------------------------");
System.out.println("System Management has finished executing.");
System.out.println("-----------------------------");
}
}
My spotify class
import java.sql.Timestamp;
/**
* Created by Rory on 11/08/2014.
*/
class Spotify extends Thread {
private Buffer buffer;
private int number;
private int bytes = 250;
public Spotify(Buffer c, int number) {
buffer = c;
this.number = number;
}
long startTime = System.currentTimeMillis();
public void run() {
for (int i = 0; i < 20; i++) {
buffer.put(bytes);
System.out.println(getName() + this.number
+ " put: " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
}
}
long endTime = System.currentTimeMillis();
long timeTaken = endTime - startTime;
java.util.Date date = new java.util.Date();
System.out.println(new Timestamp(date.getTime()));
System.out.println("-----------------------------");
System.out.println("Spotify has finished executing.");
System.out.println("Time taken to execute was " + timeTaken + " milliseconds");
System.out.println("Time that Spotify thread exited Processor was " + date);
System.out.println("-----------------------------");
}
}
I may need to add timestamps to one or two classes yet but does anyone have any idea how to get my average times to actually print out? Or what is preventing it and if the buffer limitation is effectively being shown here(given that we are talking about milliseconds?)
Thanks.
The reason why sys out's are not printing is because of the below condition in your buffer class:-
public synchronized void put(int value) {
while (process >= 1000) {
.....
notifyAll();
}
}
this condition never gets satisified as the process never is greater than 1000
This is the reason why your Processor thread also gets stuck because when it calls get() it finds that the process is less than 500 and hence it indefinitely waits when it reaches the wait() line of code.
Rectifying the process condition appropriately in your put should let your missing sys out get printed
public synchronized void put(int value) {
if(process <= 500) {
process+=value;
} else {
//while (process >= 1000) {
start = System.currentTimeMillis();
try {
wait();
} catch (InterruptedException e) {
}
end = System.currentTimeMillis();
wait = end - start;
count++;
request += wait;
System.out.println("Application Request Wait Time: " + time.format(wait));
process += value;
contents = value;
//}
}
notifyAll();
}
If you want securityupdate thread to always run at the last then the correct way of using join within that thread is as below:-
class SecurityUpdate extends Thread {
private Buffer buffer;
private int number;
private int bytes = 150;
private int process = 0;
private BubbleWitch2 bubbleWitch2;
private Spotify spotify;
private SystemManagement systemManagement;
public SecurityUpdate(Buffer c, int number, BubbleWitch2 bubbleWitch2, Spotify spotify, SystemManagement systemManagement) throws InterruptedException {
buffer = c;
this.number = number;
this.bubbleWitch2 = bubbleWitch2;
this.spotify = spotify;
this.systemManagement = systemManagement;
}
public void run() {
try {
bubbleWitch2.join();
spotify.join();
systemManagement.join();
} catch (InterruptedException e) {
}
System.out.println("Finally starting the security update");
for (int i = 0; i < 15; i++) {
buffer.put(bytes); // Paul check if it should be i or bytes
System.out.println(getName() + this.number
+ " put: " + i);
try {
sleep(1500); // Paul why is this made to sleep 1500 seconds?
} catch (InterruptedException e) {
}
}
System.out.println("-----------------------------");
System.out.println("Security Update has finished executing.");
System.out.println("------------------------------");
}
}
I'm making a class for a game I'm making and cannot figure out how to return the highest 'reward' to a user:
What I'm trying to get it to do is return the reward based on the player's kills. If he has 38 kills, it should return only 300. Not 150 aswell.
class EnumTest {
private static int playerKills = 38;
private enum KillReward {
BEGINNER(10, 150),
JUNIOR(25, 300),
EXPERT(50, 500),
CHAMPION(100, 1000);
private KillReward(int kills, int reward) {
this.kills = kills;
this.reward = reward;
}
private int kills, reward;
private int getKillsNeeded() {
return kills;
}
private int getKillReward() {
return reward;
}
}
public static void main (String[] args) {
for (KillReward k : KillReward.values()) {
if (playerKills > k.getKillsNeeded()) {
System.out.println("Kills: " + playerKills + "; Reward: " + k.getKillReward());
}
}
}
}
There are a couple ways of doing this.
One easy way is assign the reward to a variable. At the end of that loop the reward variable below will be the highest kill reward that was applicable
public static void main (String[] args) {
KillReward reward = null;
for (KillReward k : KillReward.values()) {
if (playerKills > k.getKillsNeeded()) {
reward = k;
}
}
System.out.println("Kills: " + playerKills + "; Reward: " + k.getKillReward());
}
Note that this relies on the enum being listed in order which can sometimes be fragile. For example, if a new KillReward enum is added after CHAMPION but has a lower killsNeeded value, then it will not return the proper value.
A better solution would be to create a Comparator and use it to sort the enum values by killsNeeded first, thus ensuring that they are always in order. If you also sort it in descending order, then you can also break from your loop once you hit the first applicable one.
class EnumTest {
private enum KillReward {
BEGINNER(10, 150), JUNIOR(25, 300), EXPERT(50, 500), CHAMPION(100, 1000);
// Sort KillRewards once at initialization
private static final List<KillReward> sortedKillRewards = new ArrayList<KillReward>();
static {
for (KillReward k : values())
sortedKillRewards.add(k);
Collections.sort(sortedKillRewards, new Comparator<KillReward>() {
#Override
public int compare(KillReward o1, KillReward o2) {
return (o1.kills - o2.kills) * -1; // multiplying by -1 makes it
// descending
}
});
}
private KillReward(int kills, int reward) {
this.kills = kills;
this.reward = reward;
}
private int kills, reward;
private int getKillsNeeded() {
return kills;
}
private int getKillReward() {
return reward;
}
public static KillReward forKills(int killCount) {
for (KillReward k : sortedKillRewards)
if (killCount >= k.kills)
return k;
// must not have enough kills for any reward
return null;
}
}
public static void main(String[] args) {
int kills = 9;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
kills = 10;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
kills = 38;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
}
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
how to write a Java class to sort 10 billion integers, assuming we can only fit a subset of them in memory at once.
I have done sorting but questions is how i would get the 1 billion values ?
How I am gonna sort them if i am going to load a portion of them in memory ?
If you can help me with source code it would be much appreciated.
Thanks in advance.
here is my last code, you can run it and guide me now.
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
/**
* #project jqcontacts
* #date Mar 26, 2012
* #time 11:16:35 AM
*/
public class SortIntegers {
private static String BIG_FILE="g:/bigFile.txt";
private static String SORT_FILE_PREFIX = "g:/sortFile";
/*private static final int SORT_FILE_MAX_SIZE = 1000000;*/
private static final int SORT_FILE_MAX_SIZE = 10;
private static final String MAIN_FILE = "g:/rawfile1.txt";
private static int RAWA_FILE_MAX_SIZE = 100;
// if i keep the size of MERGE_BUFFER_INITIAL_SIZE = SORT_FILE_MAX_SIZE, the end big file is sorted.
private static int MERGE_BUFFER_INITIAL_SIZE=5;
private static int MERGE_BUFFER_SIZE_NEXT = MERGE_BUFFER_INITIAL_SIZE;
private static int MERGE_BUFFER_SIZE_PREVIOUS = 0;
private static int countFile = 0;
public static void readFile(String name) throws FileNotFoundException{
Scanner scanner = new Scanner(new File(name));
List<Integer> intList = new ArrayList<Integer>();
int fileSize = 0 ;
while(scanner.hasNextInt()){
intList.add(scanner.nextInt());
++fileSize;
if(fileSize>=SORT_FILE_MAX_SIZE){
Collections.sort(intList);
/*System.out.println("list size: " + intList.size());*/
String fileName = SORT_FILE_PREFIX + countFile +".txt";
++fileSize;
PrintWriter out = openWriter(fileName);
for(int i:intList){
writeFile(i, out);
}
out.close();
intList.clear();
++countFile;
fileSize = 0;
}
}
System.out.println("done!");
}
public static List<Integer> readSortFile(String name, List<Integer> list) throws FileNotFoundException{
Scanner scanner = new Scanner(new File(name));
int bufferSize = 0;
while(scanner.hasNextInt()){
++bufferSize;
if(bufferSize>=MERGE_BUFFER_SIZE_PREVIOUS && bufferSize<=MERGE_BUFFER_SIZE_NEXT){
list.add(scanner.nextInt());
}
if(bufferSize>=MERGE_BUFFER_SIZE_NEXT){
break;
}
}
Collections.sort(list);
return list;
}
private static PrintWriter openWriter(String name) {
try {
File file = new File(name);
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)), true);
return out;
} catch (IOException e) {
//System.out.println("I/O Error");
e.printStackTrace();
System.exit(0);
}
return null;
}
private static void writeFile(int i, PrintWriter out) {
/* String line = "0" + "\t" + Integer.toString(i);*/
String line = Integer.toString(i) + "\t";
out.println(line);
}
/**
* #param args
*/
public static void main(String[] args) {
generateRawIntFile();
try {
readFile(MAIN_FILE);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("countFile: " + countFile);
// merge sort here, merge the sorted files into one
List<Integer> comboList = new ArrayList<Integer>();
boolean isDone = true;
PrintWriter outP = openWriter(BIG_FILE);
while(isDone){
for(int i=0;i<countFile;i++){
try {
//TODO: do we need the return type for readSortFile ????
comboList = readSortFile(SORT_FILE_PREFIX+i+".txt", comboList);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// System.out.println("hun writing on big file " + comboList.size());
// add the list into bigfile and clear it for further processing
try{
for(int value:comboList){
writeFile(value, outP);
}
comboList.clear();
MERGE_BUFFER_SIZE_PREVIOUS = MERGE_BUFFER_SIZE_NEXT;
MERGE_BUFFER_SIZE_NEXT += MERGE_BUFFER_INITIAL_SIZE;
System.out.println("MERGE_BUFFER_SIZE_PREVIOUS: " + MERGE_BUFFER_SIZE_PREVIOUS + " MERGE_BUFFER_SIZE_NEXT:" + MERGE_BUFFER_SIZE_NEXT);
if(MERGE_BUFFER_SIZE_PREVIOUS >= RAWA_FILE_MAX_SIZE){
System.out.println("sorting is finished");
isDone = false;
break;
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*
*/
public static void generateRawIntFile() {
Random randomGenerator = new Random();
PrintWriter out = openWriter(MAIN_FILE);
for (Integer i = 0; i < RAWA_FILE_MAX_SIZE;i++){
Integer value = randomGenerator.nextInt(RAWA_FILE_MAX_SIZE);
writeFile(value, out);
}
out.close();
}
}
There are only 4 billion possible int values so the most efficient way of doing this, is to count the number of occurrences of any value. You can use a memory MappedByteBuffer so you don't have to have 16 GB of memory. Once you have counted all the occurrences the counts will naturally be in order, so no further sorting is required. The time complexity is O(n) instead of O(n * log n) line merge sort or quick sort.
import sun.nio.ch.DirectBuffer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class Sort10Billion {
public static void main(String... args) throws IOException {
Runtime runtime = Runtime.getRuntime();
long used1 = runtime.totalMemory() - runtime.freeMemory();
MassiveCounterStore mcs = new MassiveCounterStore();
long start = System.nanoTime();
long count = 10 * 1000 * 1000 * 1000L;
for (long i = count; i > 0; i--)
mcs.incrementIndex((int) (i / 1019));
mcs.iterator(new NumberCountFunction() {
#Override
public void counted(int n, long count) {
// System.out.println(n + ": " + count);
}
});
long time = System.nanoTime() - start;
long used2 = runtime.totalMemory() - runtime.freeMemory();
System.out.printf("Took %.1f seconds to sort %,d numbers, using %.3f MB%n", time / 1e9, count, (used2-used1)/1e6);
mcs.close();
}
}
interface NumberCountFunction {
public void counted(int n, long count);
}
class MassiveCounterStore {
public static final int PARTITION_BITS = 26;
static final int PARTITIONS = (1 << (34 - PARTITION_BITS)); // 32-bit * 4 bytes.
final MappedByteBuffer[] buffers = new MappedByteBuffer[PARTITIONS];
final FileChannel channel;
int smallest = PARTITIONS;
int largest = 0;
public MassiveCounterStore() throws IOException {
File tmpStore = File.createTempFile("counter", "dat");
tmpStore.deleteOnExit();
channel = new RandomAccessFile(tmpStore, "rw").getChannel();
for (int i = 0; i < PARTITIONS; i++)
buffers[i] = channel.map(FileChannel.MapMode.READ_WRITE, (long) i << PARTITION_BITS, 1 << PARTITION_BITS);
}
public void incrementIndex(int n) {
long l = (n + Integer.MIN_VALUE) & 0xFFFFFFFFL;
int partition = (int) (l >> (PARTITION_BITS - 2)); // 4 bytes each.
int index = (int) ((l << 2) & ((1 << PARTITION_BITS) - 1));
MappedByteBuffer buffer = buffers[partition];
int count = buffer.getInt(index);
buffer.putInt(index, count + 1);
if (smallest > partition) smallest = partition;
if (largest < partition) largest = partition;
}
public void iterator(NumberCountFunction nfc) {
int n = (smallest << (PARTITION_BITS -2)) + Integer.MIN_VALUE;
for (int p = smallest; p <= largest; p++) {
MappedByteBuffer buffer = buffers[p];
for (int i = 0; i < 1 << PARTITION_BITS; i += 4) {
int count = buffer.getInt(i);
if (count != 0)
nfc.counted(n, count & 0xFFFFFFFFL);
n++;
}
}
assert n == Integer.MIN_VALUE;
}
public void close() {
try {
channel.close();
} catch (IOException ignored) {
}
for (MappedByteBuffer buffer : buffers) {
((DirectBuffer) buffer).cleaner().clean();
}
}
}
prints when run with -XX:-UseTLAB (which gives you more accurate memory usage)
Took 150.7 seconds to sort 10,000,000,000 numbers, using 0.202 MB
I think using 202 KB is pretty good. ;)
Note: your performance is heavily dependant on the distribution of values as this impacts the efficiency of the cache.