Skip to main content
added 33 characters in body
Source Link
bowmore
  • 5.4k
  • 1
  • 20
  • 23

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better, and we'll throw a custom exception.

The idea of the algotithmalgorithm is that we have a current choice, and for each new input we may take the new inputit as our current choice instead. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs)[ and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs) and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better, and we'll throw a custom exception.

The idea of the algorithm is that we have a current choice, and for each new input we may take it as our current choice instead. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs[ and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}
deleted 3 characters in body
Source Link
bowmore
  • 5.4k
  • 1
  • 20
  • 23

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs) and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new NoSuchElementExceptionEmptyInputException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs) and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new NoSuchElementException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs) and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}
Rewrote with generics and fixed some bugs
Source Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<Integer>Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the numberitem we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, andfand that chance is 1/totalInputstotalInputs. So we ask our random source to get the next int in [0, totalInputs[totalInputs) and if it turns out to be 0, we change our currentPickcurrentPick to the latest numberitem.

so something like this :

public intstatic pickNumber<T> T pickItem(Iterator<Integer>Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputExceptionNoSuchElementException();
    }

    intT currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        intT next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(intlong totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<Integer> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the number we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, andf that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs[ and if it turns out to be 0, we change our currentPick to the latest number.

so something like this :

public int pickNumber(Iterator<Integer> inputs) {
    if (!inputs.hasNext()) {
        throw new EmptyInputException();
    }

    int currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        int next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private boolean shouldPick(int totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}

I'll focus on the choice of algorithm. Choose janos' answer for style review, I'd say.

Files can be arbitrarily large. Larger than what can actually fit in your VM's memory. Your algorithm, while simple, does not deal well with a source of numbers of which the size is not known at the start.

So lets devise a better algorithm that can deal with an arbitrarily large number of inputs, of which we do not know the size beforehand.

To abstract this we will act as if our function gets as input an Iterator<T> (it could be one that reads from a file, or simply iterates a list, or reads primes from a webservice, ...)

First off, we'll need to handle the case that the Iterator is empty. Your current solution simply throws an IndexOutOfBoundsException, but we can do better.

The idea of the algotithm is that we have a current choice, and for each new input we may take the new input as our current choice. Of course, the odds that we should change our current choice should decrease proportionally to the number of inputs we've seen.

We'll need to keep track of a few things :

  • the item we would have picked if we'd have seen all inputs : currentPick
  • the number of inputs we've seen so far : totalInputs

When we read the first number it's the only one we can pick so far, so currentPick becomes that number and we increase totalInputs.

Then for every next input, we determine the chance that that number becomes our currentPick, and that chance is 1/totalInputs. So we ask our random source to get the next int in [0, totalInputs) and if it turns out to be 0, we change our currentPick to the latest item.

so something like this :

public static <T> T pickItem(Iterator<T> inputs) {
    if (!inputs.hasNext()) {
        throw new NoSuchElementException();
    }

    T currentPick = inputs.next();
    long totalInputs = 1;

    Random random = new Random();

    while (inputs.hasNext()) {
        T next = inputs.next();
        totalInputs++;
        if (shouldPick(totalInputs, random)) {
            currentPick = next;
        }
    }
    return currentPick;
}

private static boolean shouldPick(long totalInputs, Random random) {
    return nextLong(random, totalInputs) == 0;
}

private static long nextLong(Random random, long bound) {
    return (long) (random.nextDouble() * bound);
}
Source Link
bowmore
  • 5.4k
  • 1
  • 20
  • 23
Loading