I'm using a Map with eligible words for a hangman game I'm developing. The Integer in the Map stores the times a word has been chosen, so in the beginning the Map looks like this:
alabanza 0
esperanza 0
comunal 0
aprender 0
....
After some plays, the Map would look like this
alabanza 3
esperanza 4
comunal 3
aprender 1
....
I'd like to choose the next word randomly but having the less chosen word a bigger probability of been chosen.
I've read Java - Choose a random value from a HashMap but one with the highest integer assigned but it's the opposite case.
I''ve also thought I could use a list with repeated words (the more times a word appears in the list, the more the probabilities of been chosen) but I've only managed to get to this:
int numberOfWords=wordList.size(); //The Map
List<String> repeatedWords=new ArrayList<>();
for (Map.Entry<String,Integer> entry : wordList.entrySet()) {
for (int i = 0; i < numberOfWords-entry.getValue(); i++) {
repeatedWords.add(entry.getKey());
}
}
Collections.shuffle(repeatedWords); //Should it be get(Random)?
String chosenWord=repeatedWords.get(0);
I think this fails when the amount of words chosen equals the amount of words.
Edit
Finally there's a problem with the probability of each word once they have different numbers. I've changed the point of view so I first put a probability of 1000 (It could be any number) and every time I choose a word, I reduce the probability a certain amount (let's say, 20%), so I use:
wordList.put(chosen,(int)(wordList.get(chosen)*0.8)+1);
After that I choose the word with the recipe Lajos Arpad or Ahmad Shahwan gave.
If the game were to be played many many times, all the probabilities would tend to 1, but that's not my case.
Thanks all who answered.
Try this:
import java.util.Map;
import java.util.HashMap;
import java.util.Random;
public class MyClass {
public static void main(String args[]) {
Map<String, Integer> wordList = new HashMap<>();
wordList.put("alabanza", 3);
wordList.put("esperanza", 4);
wordList.put("comunal", 3);
wordList.put("aprender", 1);
Map<String, Integer> results = new HashMap<>(4);
for (int i = 0; i < 100; i++) {
String name = randomize(wordList);
Integer old = results.getOrDefault(name, 0);
results.put(name, old + 1);
}
for (Map.Entry<String, Integer> e : results.entrySet()) {
System.out.println(e.getKey() + "\t" + e.getValue());
}
}
private static String randomize(Map<String, Integer> wordList) {
final Integer sum = wordList.values().stream().reduce(Integer::sum).orElse(0);
final int grandSum = (wordList.size() - 1) * sum;
final int random = new Random().nextInt(grandSum + 1);
int index = 0;
for (Map.Entry<String, Integer> e: wordList.entrySet()) {
index += (sum - e.getValue());
if (index >= random) {
return e.getKey();
}
}
return null;
}
}
Out put is the times a name was chosen over 100 trial:
aprender 37
alabanza 25
comunal 23
esperanza 15
You can try it yourself here.
I won't provide exact code, but basic idea.
Iterate over wordList.values() to find the maximum weight M and sum of weights S.
Now let each word w have likelihood (like probability, but they don't have to sum to 1) to be chosen M + 1 - wordList.get(w), so a word with weight 1 is M times more likely to be chosen than a word with weight M.
The sum of likelihoods will be (M + 1) * wordList.size() - S (that's why we need S). Pick a random number R between 0 and this sum.
Iterate over wordList.entrySet(), summing likelihoods as you go. When the sum passes R, that's the word you want.
Your map values are your weights.
You need to pick an integer lower than the weights sum.
You pick each String entry, with its weight. When the weight sum passes the random integer, you are on THE String.
This will give you :
public static void main(String ... args){
Map<String, Integer> wordList = new HashMap<>();
wordList.put("foo", 4);
wordList.put("foo2", 2);
wordList.put("foo3", 7);
System.out.println(randomWithWeight(wordList));
}
public static String randomWithWeight(Map<String, Integer> weightedWordList) {
int sum = weightedWordList.values().stream().collect(Collectors.summingInt(Integer::intValue));
int random = new Random().nextInt(sum);
int i = 0;
for (Map.Entry<String, Integer> e : weightedWordList.entrySet()){
i += e.getValue();
if (i > random){
return e.getKey();
}
}
return null;
}
For the sake of simplicity let us suppose that you have an array called occurrences, which has int elements (you will easily translate this into your data structure).
Now, let's find the maximum value:
int max = 0;
for (int i = 0; i < occurrences.length; i++) {
if (max < occurrences[i]) max = occurrences[i];
}
Let's increment it:
max++;
Now, let's give a weight of max to the items which have 0 as value, a weight of max - 1 to the items which occurred once, and so on (no item will have a weight of 0 since we incremented max):
int totalWeight = 0;
for (int j = 0; j < occurrences.length; j++) {
totalWeight += max - occurrences[j];
}
Note that all items will have their weight. Now, let's suppose you have a randomized integer, called r, where 0 < r <= totalWeight:
int resultIndex = -1;
for (int k = 0; (resultIndex < 0) && k < occurrences.length; k++) {
if (r <= max - occurrences[k]) resultIndex = k;
else r -= max - occurrences[k];
}
and the result is occurrences[resultIndex]
Related
I'm working on a project that prompts the user to create and fill an array with integers, then displays the mean, mode, median, and standard deviation of that array. It starts by asking the user what the size of the array will be, to which the number entered will declare and initialize the array. The program will then iterate several times asking the user to declare an integer value, and each value will be stored into the array until the array is filled. The program will then print the contents of the array, as well as the mean, mode, median, and standard deviation.
I have a code that seems to meet all these requirements. However, one thing I am struggling on is the mode. While it does print out the most repeated number in the array, it doesn't take into account multiple modes with the same number of repetitions, nor does it take into account what will happen if there is no mode.
Right now, if two numbers are entered twice each, the mode displayed is the first number to be repeated more than once. For example, if I have an array size of 10 integers, and the integers I enter are 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, it will print out "2.0" for the mode instead of printing both "2.0" and "3.0." If there is no mode, it simply enters the number first entered, rather than saying "None."
What would be the best course of action to go about accomplishing this?
Here is my code:
import java.util.*;
public class ArrayStatistics {
public static void main(String[] args) {
double total = 0;
Scanner input = new Scanner(System.in);
System.out.print("Enter the size of your array >> ");
int size = input.nextInt();
double[] myArray = new double[size];
System.out.print("Enter the integer values >> ");
for (int i=0; i<size; i++) {
myArray[i] = input.nextInt();
}
System.out.println("\nIntegers:");
for (int i=0; i<size; i++) {
System.out.println(myArray[i]);
}
double mean = calculateMean(myArray);
System.out.println("\nMean: " + mean);
double mode = calculateMode(myArray);
System.out.println("Mode: " + mode);
double median = calculateMedian(myArray);
System.out.println("Median: " + median);
double SD = calculateSD(myArray);
System.out.format("Standard Deviation: %.6f", SD);
}
public static double calculateMean(double myArray[]) {
int sum = 0;
for(int i = 0; i<myArray.length; i++) {
sum = (int) (sum + myArray[i]);
}
double mean = ((double) sum) / (double)myArray.length;
return mean;
}
public static double calculateMode(double myArray[]) {
int modeCount = 0;
int mode = 0;
int currCount = 0;
for(double candidateMode : myArray) {
currCount = 0;
for(double element : myArray) {
if(candidateMode == element) {
currCount++;
}
}
if(currCount > modeCount) {
modeCount = currCount;
mode = (int) candidateMode;
}
}
return mode;
}
public static double calculateMedian(double myArray[]) {
Arrays.sort(myArray);
int val = myArray.length/2;
double median = ((myArray[val]+myArray[val-1])/2.0);
return median;
}
public static double calculateSD(double myArray[]) {
double sum = 0.0;
double standardDeviation = 0.0;
int length = myArray.length;
for(double num : myArray) {
sum += num;
}
double mean = sum/length;
for(double num : myArray) {
standardDeviation += Math.pow(num - mean, 2);
}
return Math.sqrt(standardDeviation/length);
}
First the code, then the explanations.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;
public class ArrayStatistics {
public static void main(String[] args) {
int total = 0;
Scanner input = new Scanner(System.in);
System.out.print("Enter the size of your array >> ");
int size = input.nextInt();
int[] myArray = new int[size];
Map<Integer, Integer> frequencies = new HashMap<>();
System.out.print("Enter the integer values >> ");
for (int i = 0; i < size; i++) {
myArray[i] = input.nextInt();
if (frequencies.containsKey(myArray[i])) {
int frequency = frequencies.get(myArray[i]);
frequencies.put(myArray[i], frequency + 1);
}
else {
frequencies.put(myArray[i], 1);
}
total += myArray[i];
}
System.out.println("\nIntegers:");
for (int i = 0; i < size; i++) {
System.out.println(myArray[i]);
}
double mean = calculateMean(size, total);
System.out.println("\nMean: " + mean);
List<Integer> mode = calculateMode(frequencies);
System.out.println("Mode: " + mode);
double median = calculateMedian(myArray);
System.out.println("Median: " + median);
double stdDev = calculateSD(mean, total, size, myArray);
System.out.format("Standard Deviation: %.6f", stdDev);
}
public static double calculateMean(int count, int total) {
double mean = ((double) total) / count;
return mean;
}
public static List<Integer> calculateMode(Map<Integer, Integer> frequencies) {
Map<Integer, Integer> sorted = frequencies.entrySet()
.stream()
.sorted((e1, e2) -> e2.getValue() - e1.getValue())
.collect(Collectors.toMap(e -> e.getKey(),
e -> e.getValue(),
(i1, i2) -> i1,
LinkedHashMap::new));
Iterator<Integer> iterator = sorted.keySet().iterator();
Integer first = iterator.next();
Integer val = sorted.get(first);
List<Integer> modes = new ArrayList<>();
if (val > 1) {
modes.add(first);
while (iterator.hasNext()) {
Integer next = iterator.next();
Integer nextVal = sorted.get(next);
if (nextVal.equals(val)) {
modes.add(next);
}
else {
break;
}
}
}
return modes;
}
public static double calculateMedian(int myArray[]) {
Arrays.sort(myArray);
int val = myArray.length / 2;
double median = ((myArray[val] + myArray[val - 1]) / 2.0);
return median;
}
public static double calculateSD(double mean, int sum, int length, int[] myArray) {
double standardDeviation = 0.0;
for (double num : myArray) {
standardDeviation += Math.pow(num - mean, 2);
}
return Math.sqrt(standardDeviation / length);
}
}
In order to determine the mode(s), you need to keep track of the occurrences of integers entered into your array. I use a Map to do this. I also calculate the total while entering the integers. I use this total in methods that require it, for example calculateMean. Seems like extra work to recalculate the total each time you need it.
You are dealing with integers, so why declare myArray as array of double? Hence I changed it to array of int.
Your question was how to determine the mode(s). Consequently I refactored method calculatMode. In order to determine the mode(s), you need to interrogate the frequencies, hence the method parameter. Since you claim that there can be zero, one or more than one modes, the method returns a List. First I sort the Map entries according to the value, i.e. the number of occurrences of a particular integer in myArray. I sort the entries in descending order. Then I collect all the sorted entries to a LinkedHashMap since that is a map that stores its entries in the order in which they were added. Hence the first entry in the LinkedHashMap will be the integer with the most occurrences. If the number of occurrences of the first map entry is 1 (one), that means there are no modes (according to this definition that I found):
If no number in the list is repeated, then there is no mode for the list.
In the case of no modes, method calculateMode returns an empty List.
If the number of occurrences of the first entry is more than one, I add the integer to the List. Then I iterate through the remaining map entries and add the integer to the List if its occurrences equals that of the first map entry. As soon as the number of occurrences in an entry does not equal that of the first entry, I exit the while loop. Now List contains all the integers in myArray with the highest number of occurrences.
Here is a sample run (using example data from your question):
Enter the size of your array >> 10
Enter the integer values >> 1 2 2 3 3 4 5 6 7 8
Integers:
1
2
2
3
3
4
5
6
7
8
Mean: 4.1
Mode: [2, 3]
Median: 3.5
Standard Deviation: 2.211334
As in the title, I want to use Knuth-Fisher-Yates shuffle algorithm to select N random elements from a List but without using List.toArray and change the list. Here is my current code:
public List<E> getNElements(List<E> list, Integer n) {
List<E> rtn = null;
if (list != null && n != null && n > 0) {
int lSize = list.size();
if (lSize > n) {
rtn = new ArrayList<E>(n);
E[] es = (E[]) list.toArray();
//Knuth-Fisher-Yates shuffle algorithm
for (int i = es.length - 1; i > es.length - n - 1; i--) {
int iRand = rand.nextInt(i + 1);
E eRand = es[iRand];
es[iRand] = es[i];
//This is not necessary here as we do not really need the final shuffle result.
//es[i] = eRand;
rtn.add(eRand);
}
} else if (lSize == n) {
rtn = new ArrayList<E>(n);
rtn.addAll(list);
} else {
log("list.size < nSub! ", lSize, n);
}
}
return rtn;
}
It uses list.toArray() to make a new array to avoid modifying the original list. However, my problem now is that my list could be very big, can have 1 million elements. Then list.toArray() is too slow. And my n could range from 1 to 1 million. When n is small (say 2), the function is very in-efficient as it still need to do list.toArray() for a list of 1 million elements.
Can someone help improve the above code to make it more efficient when dealing with large lists. Thanks.
Here I assume Knuth-Fisher-Yates shuffle is the best algorithm to do the job of selecting n random elements from a list. Am I right? I would be very glad to if there is other algorithms better than Knuth-Fisher-Yates shuffle to do the job in terms of the speed and the quality of the results (guarantee real randomness).
Update:
Here is some of my test results:
When selection n from 1000000 elements.
When n<1000000/4 the fastest way to through using Daniel Lemire's Bitmap function to select n random id first then get the elements with these ids:
public List<E> getNElementsBitSet(List<E> list, int n) {
List<E> rtn = new ArrayList<E>(n);
int[] ids = genNBitSet(n, 0, list.size());
for (int i = 0; i < ids.length; i++) {
rtn.add(list.get(ids[i]));
}
return rtn;
}
The genNBitSet is using the code generateUniformBitmap from https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2013/08/14/java/UniformDistinct.java
When n>1000000/4 the Reservoir Sampling method is faster.
So I have built a function to combine these two methods.
You are probably looking for something like Resorvoir Sampling.
Start with an initial array with first k elements, and modify it with new elements with decreasing probabilities:
java like pseudo code:
E[] r = new E[k]; //not really, cannot create an array of generic type, but just pseudo code
int i = 0;
for (E e : list) {
//assign first k elements:
if (i < k) { r[i++] = e; continue; }
//add current element with decreasing probability:
j = random(i++) + 1; //a number from 1 to i inclusive
if (j <= k) r[j] = e;
}
return r;
This requires a single pass on the data, with very cheap ops every iteration, and the space consumption is linear with the required output size.
If n is very small compared to the length of the list, take an empty set of ints and keep adding a random index until the set has the right size.
If n is comparable to the length of the list, do the same, but then return items in the list that don't have indexes in the set.
In the middle ground, you can iterate through the list, and randomly select items based on how many items you've seen, and how many items you've already returned. In pseudo-code, if you want k items from N:
for i = 0 to N-1
if random(N-i) < k
add item[i] to the result
k -= 1
end
end
Here random(x) returns a random number between 0 (inclusive) and x (exclusive).
This produces a uniformly random sample of k elements. You could also consider making an iterator to avoid building the results list to save memory, assuming the list is unchanged as you're iterating over it.
By profiling, you can determine the transition point where it makes sense to switch from the naive set-building method to the iteration method.
Let's assume that you can generate n random indices out of m that are pairwise disjoint and then look them up efficiently in the collection. If you don't need the order of the elements to be random, then you can use an algorithm due to Robert Floyd.
Random r = new Random();
Set<Integer> s = new HashSet<Integer>();
for (int j = m - n; j < m; j++) {
int t = r.nextInt(j);
s.add(s.contains(t) ? j : t);
}
If you do need the order to be random, then you can run Fisher--Yates where, instead of using an array, you use a HashMap that stores only those mappings where the key and the value are distinct. Assuming that hashing is constant time, both of these algorithms are asymptotically optimal (though clearly, if you want to randomly sample most of the array, then there are data structures with better constants).
Just for convenience: A MCVE with an implementation of the Resorvoir Sampling proposed by amit (possible upvotes should go to him (I'm just hacking some code))
It seems like this is indeed a algorithm that nicely covers the cases of where the number of elements to select is low compared to the list size, and the cases where the number of elements is high compared to the list size (assumung that the properties about the randomness of the result that are stated on the wikipedia page are correct).
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.TreeMap;
public class ReservoirSampling
{
public static void main(String[] args)
{
example();
//test();
}
private static void test()
{
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
int size = 2;
int runs = 100000;
Map<String, Integer> counts = new TreeMap<String, Integer>();
for (int i=0; i<runs; i++)
{
List<String> sample = sample(list, size);
String s = createString(sample);
Integer count = counts.get(s);
if (count == null)
{
count = 0;
}
counts.put(s, count+1);
}
for (Entry<String, Integer> entry : counts.entrySet())
{
System.out.println(entry.getKey()+" : "+entry.getValue());
}
}
private static String createString(List<String> list)
{
Collections.sort(list);
StringBuilder sb = new StringBuilder();
for (String s : list)
{
sb.append(s);
}
return sb.toString();
}
private static void example()
{
List<String> list = new ArrayList<String>();
for (int i=0; i<26; i++)
{
list.add(String.valueOf((char)('A'+i)));
}
for (int i=1; i<=26; i++)
{
printExample(list, i);
}
}
private static <T> void printExample(List<T> list, int size)
{
System.out.printf("%3d elements: "+sample(list, size)+"\n", size);
}
private static final Random random = new Random(0);
private static <T> List<T> sample(List<T> list, int size)
{
List<T> result = new ArrayList<T>(Collections.nCopies(size, (T) null));
int i = 0;
for (T element : list)
{
if (i < size)
{
result.set(i, element);
i++;
continue;
}
i++;
int j = random.nextInt(i);
if (j < size)
{
result.set(j, element);
}
}
return result;
}
}
If n is way smaller then size, you could use this algorith, witch is unfortunatly quadratic with n, but doest depend on size of array at all.
Example with size = 100 and n = 4.
choose random number from 0 to 99, lets say 42, and add it to result.
choose random number from 0 to 98, lets say 39, and add it to result.
choose random number from 0 to 97, lets say 41, but since 41 is bigger or equal than 39, increment it by 1, so you have 42, but that is bigger then equal than 42, so you have 43.
...
Shortly, you choose from remaining numbers and then compuce what number have you acctualy chosen. I would use link list for this, but maybe there are better data structures.
Summarizing Changwang's update. If you want more than 250,000 items, use amit's answer. Otherwise use Knuth-Fisher-Yates Shuffle as shown in entirety here
NOTE: The result is always in the original order as well
public static <T> List<T> getNRandomElements(int n, List<T> list) {
List<T> subList = new ArrayList<>(n);
int[] ids = generateUniformBitmap(n, list.size());
for (int id : ids) {
subList.add(list.get(id));
}
return subList;
}
// https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2013/08/14/java/UniformDistinct.java
private static int[] generateUniformBitmap(int num, int max) {
if (num > max) {
DebugUtil.e("Can't generate n ints");
}
int[] ans = new int[num];
if (num == max) {
for (int k = 0; k < num; ++k) {
ans[k] = k;
}
return ans;
}
BitSet bs = new BitSet(max);
int cardinality = 0;
Random random = new Random();
while (cardinality < num) {
int v = random.nextInt(max);
if (!bs.get(v)) {
bs.set(v);
cardinality += 1;
}
}
int pos = 0;
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
ans[pos] = i;
pos += 1;
}
return ans;
}
If you want them randomized, I use:
public static <T> List<T> getNRandomShuffledElements(int n, List<T> list) {
List<T> randomElements = getNRandomElements(n, list);
Collections.shuffle(randomElements);
return randomElements;
}
I needed something for this in C#, here's my solution which works on a generic List.
It selects N random elements of the list and places them at the front of the list.
So upon returning, the first N elements of the list are randomly selected. It is fast and efficient even when you're dealing with a very large number of elements.
static void SelectRandom<T>(List<T> list, int n)
{
if (n >= list.Count)
{
// n should be less than list.Count
return;
}
int max = list.Count;
var random = new Random();
for (int i = 0; i < n; i++)
{
int r = random.Next(max);
max = max - 1;
int irand = i + r;
if (i != irand)
{
T rand = list[irand];
list[irand] = list[i];
list[i] = rand;
}
}
}
I have a map as shown below:
Key Value
23 20
32 20 (20+20 =40 , min=23 max=32)
43 18
45 24 (24+18 =42 , since 42 >40 so here min and max will be same that is 43
47 10
56 6 (24 +10 +6 =40) so here min =45 and max = 56
43 50 ****so how we will handle the case where value is greater than key 50 >43 ********
so i have implemented the logic which will take the
1) where value of map value reaches 40
2) where the map value upon calculation becomes greater than 40
3) ** haven't implemented the scenario in where first instance where the value of map at initial level is greater at first instance let say as shown above the key is 43 and the value is 50**
Now please advise how to handle the third scenario, what I have implemented is ..
create a Pair class that will hold the key and the value.
class Pair {
public int key;
public int value;
public Pair(int key, int value){
this.key = key;
this.value = value;
}
}
hen create a list of pair and iterate through it. If the sum is 0, initialize the min and the max. Then for each pair iterated, add its value to the sum. If the sum is inferior continue the loop and update the max key, else you have two cases possible:
The sum is equals to the limit so update the max key
The sum is not equals to the limit (so it's superior), decrement the index and don't update the max key
public static void main(String[] arg) {
Map<Long, Integer> m = new LinkedHashMap<>();
//fill your map here
List<Pair> l = new ArrayList<>();
for(Map.Entry<Long, Integer> entries : m.entrySet()){
l.add(new Pair(entries.getKey(), entries.getValue()));
}
//Now you have a list of Pair
int sum = 0;
int min = -1;
int max = -1;
for(int i = 0; i < pairList.size(); i++){
Pair p = pairList.get(i);
if(sum == 0){
min = p.key;
max = p.key;
}
sum += p.value;
if(sum < LIMIT){
max = p.key;
} else {
if(sum > LIMIT){
i--;
} else {
max = p.key;
}
System.out.println(min+"_"+max);
sum = 0;
}
}
}
Which prints:
23_32
43_43
45_56
Can you please advise how to handle the third scenario where first instance where the value of map at initial level is greater at first instance let say as shown above the key is 43 and the value is 50**
You still did not really specify clearly what you are going to achieve. You did not say whether the keys of your input map are in ascending order (that is, whether it is a TreeMap). In the original question (linked in the first comment), your input was 2 lists. The example that you posted does not make any sense at all, because it contains the key=43 twice - so it can't be a map. Your description sounds like there is some constraint between the keys and the values (the value 50 being greater than the key 43), but maybe this is just an artifact of your explaination.
Your solution approach, and how you updated some variables in your implementation, may be an attempt to give precise information. But maybe a verbal or semi-formal description of your actual goal are more helpful here. Maybe you just can not explain what you want to achieve. In the worst case, you don't know it.
At the moment, my interpretation is roughly the following: Your input consists of two lists (!). And you are trying to find ranges of the first list, so that the sum of the corresponding values in the second list is at least 40.
IF this is the case, you can just compute the indices for the list of values where the summation should start and where the summation should end. When you have these indices, you can obtain the corresponding "keys" from the first list.
import java.util.ArrayList;
import java.util.List;
public class SumSplit
{
public static void main(String[] args)
{
List<Integer> keys = new ArrayList<Integer>();
keys.add(23);
keys.add(32);
keys.add(43);
keys.add(45);
keys.add(47);
keys.add(56);
keys.add(43);
List<Integer> values = new ArrayList<Integer>();
values.add(20);
values.add(20);
values.add(18);
values.add(24);
values.add(10);
values.add( 6);
values.add(50);
final int SPLIT_VALUE = 40;
List<Integer> minIndices = new ArrayList<Integer>();
List<Integer> maxIndices = new ArrayList<Integer>();
int sum = 0;
int minIndex = -1;
for (int i=0; i<keys.size(); i++)
{
Integer value = values.get(i);
sum += value;
if (minIndex == -1)
{
minIndex = i;
}
if (sum >= SPLIT_VALUE)
{
minIndices.add(minIndex);
maxIndices.add(i);
minIndex = -1;
sum = 0;
}
}
for (int i=0; i<minIndices.size(); i++)
{
Integer min = minIndices.get(i);
Integer max = maxIndices.get(i);
System.out.println("min: "+keys.get(min)+", max "+keys.get(max));
printInfo(min, max, keys, values);
}
}
private static void printInfo(int min, int max, List<Integer> keys, List<Integer> values)
{
int sum = 0;
for (int i=min; i<=max; i++)
{
Integer key = keys.get(i);
Integer value = values.get(i);
sum += value;
System.out.println(" "+key+" : "+value+" (sum until now: "+sum+")");
}
System.out.println("Sum: "+sum);
}
}
If this is not what you are going to achieve, please describe clearly what your actual goal is.
I have an array of numbers and I need the biggest of three number with respective index value. I have an array like this:
int [] value = new int[5];
value[0] = 8;
value[1] = 3;
value[2] = 5;
value[3] = 2;
value[4] = 7;
How to find the largest numbers and their index values?
I suspsect this is homework, so I'm going to give some help, but not a full solution.
You need the biggest three numbers, as well as their index values?
Well, walk over the array, keeping track of the highest three numbers you have found so far. Also keep track of their index numbers.
You could start by doing this for only the biggest number and its index. That should be easy.
It takes two variables, e.g. BiggestNumber and indexOfBiggestNumber. Start with finding the biggest number (trivial), then add some code to remember it's index.
Once you have that, you can add some more code to keep track of the second biggest number and it's index as well.
After that, you do the same for the third biggest number.
I have done it for you, and this works.
here goes the complete code:
import java.util.Arrays;
class tester {
public static void main(String[] args) {
int[] value = new int[5];
value[0] = 8;
value[1] = 3;
value[2] = 5;
value[3] = 2;
value[4] = 7;
int size = value.length;
int[] temp = (int[]) value.clone();
Arrays.sort(temp);
for (int i = 0; i < 3; i++) {
System.out.println("value: " + temp[size - (i + 1)] +
" index " + getIndex(value, temp[size - (i + 1)]));
}
}
static int getIndex(int[] value, int v) {
int temp = 0;
for (int i = 0; i < value.length; i++) {
if (value[i] == v) {
temp = i;
break;
}
}
return temp;
}
}
No need to traverse through array and keep tracking of so many variables , you can take advantage of already implemented methods like below.
I would suggest to use a List of Map.Entry<key,value > (where key=index and value=number) and then implement Comparator interface with overridden compare method (to sort on values). Once you have implemented it just sort the list .
public static void main(String[] args) {
int[] value = {5, 3, 12, 12, 7};
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int k = 0; k < value.length; k++)
map.put(k, value[k]);
List<Map.Entry<Integer, Integer>> list =
new LinkedList<Map.Entry<Integer, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
#Override
public int compare(
Entry<Integer, Integer> e1,
Entry<Integer, Integer> e2) {
return e2.getValue().compareTo(e1.getValue());
}
});
for (Entry<Integer, Integer> lValue : list)
System.out.println("value = " + lValue.getValue()
+ " , Index = " + lValue.getKey());
}
Results:
value = 12 , Index = 2
value = 12 , Index = 3
value = 7 , Index = 4
value = 5 , Index = 0
value = 3 , Index = 1
By this approach you can get top N largest numbers with their index.
To get the three biggest, basically, you sort, and pick the last three entries.
Getting their indexes takes a little more work, but is definitely doable. Simply bundle the number and its index together in a Comparable whose compareTo function only cares about the number. Sort, get the last three items, and now you have each number and its index.
class IntWithIndex implements Comparable<IntWithIndex> {
public int number, index;
public IntWithIndex(number, index) {
this.number = number;
this.index = index;
}
public int compareTo(IntWithIndex other) {
return number - other.number;
}
}
...
IntWithIndex iwi[] = new IntWithIndex[yourNumbers.length];
for (int i = 0; i < yourNumbers.length; ++i) {
iwi[i] = new IntWithIndex(yourNumbers[i], i);
}
Arrays.sort(iwi);
int largest = iwi[iwi.length - 1].number;
int largestIndex = iwi[iwi.length - 1].index;
// and so on
Sort the array in descending order and show the first 3 element.
Note: Version 2, below, uses the Sieve of Eratosthenes. There are several answers that helped with what I originally asked. I have chosen the Sieve of Eratosthenes method, implemented it, and changed the question title and tags appropriately. Thanks to everyone who helped!
Introduction
I wrote this fancy little method that generates an array of int containing the prime numbers less than the specified upper bound. It works very well, but I have a concern.
The Method
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
int [] primes = new int [index];
while(--index >= 0) {
primes [index] = temp [index];
}
return primes;
}
My Concern
My concern is that I am creating an array that is far too large for the final number of elements the method will return. The trouble is that I don't know of a good way to correctly guess the number of prime numbers less than a specified number.
Focus
This is how the program uses the arrays. This is what I want to improve upon.
I create a temporary array that is
large enough to hold every number
less than the limit.
I generate the prime numbers, while
keeping count of how many I have
generated.
I make a new array that is the right
dimension to hold just the prime
numbers.
I copy each prime number from the
huge array to the array of the
correct dimension.
I return the array of the correct
dimension that holds just the prime
numbers I generated.
Questions
Can I copy the whole chunk (at once) of
temp[] that has nonzero
elements to primes[]
without having to iterate through
both arrays and copy the elements
one by one?
Are there any data structures that
behave like an array of primitives
that can grow as elements are added,
rather than requiring a dimension
upon instantiation? What is the
performance penalty compared to
using an array of primitives?
Version 2 (thanks to Jon Skeet):
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
return Arrays.copyOfRange(temp, 0, index);
}
Version 3 (thanks to Paul Tomblin) which uses the Sieve of Erastosthenes:
private static int [] generatePrimes(int max) {
boolean[] isComposite = new boolean[max + 1];
for (int i = 2; i * i <= max; i++) {
if (!isComposite [i]) {
for (int j = i; i * j <= max; j++) {
isComposite [i*j] = true;
}
}
}
int numPrimes = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) numPrimes++;
}
int [] primes = new int [numPrimes];
int index = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) primes [index++] = i;
}
return primes;
}
Your method of finding primes, by comparing every single element of the array with every possible factor is hideously inefficient. You can improve it immensely by doing a Sieve of Eratosthenes over the entire array at once. Besides doing far fewer comparisons, it also uses addition rather than division. Division is way slower.
ArrayList<> Sieve of Eratosthenes
// Return primes less than limit
static ArrayList<Integer> generatePrimes(int limit) {
final int numPrimes = countPrimesUpperBound(limit);
ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
boolean [] isComposite = new boolean [limit]; // all false
final int sqrtLimit = (int)Math.sqrt(limit); // floor
for (int i = 2; i <= sqrtLimit; i++) {
if (!isComposite [i]) {
primes.add(i);
for (int j = i*i; j < limit; j += i) // `j+=i` can overflow
isComposite [j] = true;
}
}
for (int i = sqrtLimit + 1; i < limit; i++)
if (!isComposite [i])
primes.add(i);
return primes;
}
Formula for upper bound of number of primes less than or equal to max (see wolfram.com):
static int countPrimesUpperBound(int max) {
return max > 1 ? (int)(1.25506 * max / Math.log((double)max)) : 0;
}
Create an ArrayList<Integer> and then convert to an int[] at the end.
There are various 3rd party IntList (etc) classes around, but unless you're really worried about the hit of boxing a few integers, I wouldn't worry about it.
You could use Arrays.copyOf to create the new array though. You might also want to resize by doubling in size each time you need to, and then trim at the end. That would basically be mimicking the ArrayList behaviour.
Algo using Sieve of Eratosthenes
public static List<Integer> findPrimes(int limit) {
List<Integer> list = new ArrayList<>();
boolean [] isComposite = new boolean [limit + 1]; // limit + 1 because we won't use '0'th index of the array
isComposite[1] = true;
// Mark all composite numbers
for (int i = 2; i <= limit; i++) {
if (!isComposite[i]) {
// 'i' is a prime number
list.add(i);
int multiple = 2;
while (i * multiple <= limit) {
isComposite [i * multiple] = true;
multiple++;
}
}
}
return list;
}
Image depicting the above algo (Grey color cells represent prime number. Since we consider all numbers as prime numbers intially, the whole is grid is grey initially.)
Image Source: WikiMedia
The easiest solution would be to return some member of the Collections Framework instead of an array.
Are you using Java 1.5? Why not return List<Integer> and use ArrayList<Integer>? If you do need to return an int[], you can do it by converting List to int[] at the end of processing.
As Paul Tomblin points out, there are better algorithms.
But keeping with what you have, and assuming an object per result is too big:
You are only ever appending to the array. So, use a relatively small int[] array. When it's full use append it to a List and create a replacement. At the end copy it into a correctly sized array.
Alternatively, guess the size of the int[] array. If it is too small, replace by an int[] with a size a fraction larger than the current array size. The performance overhead of this will remain proportional to the size. (This was discussed briefly in a recent stackoverflow podcast.)
Now that you've got a basic sieve in place, note that the inner loop need only continue until temp[i]*temp[i] > prime.
I have a really efficient implementation:
we don't keep the even numbers, therefore halving the memory usage.
we use BitSet, requiring only one bit per number.
we estimate the upper bound for number of primes on the interval, thus we can set the initialCapacity for the Array appropriately.
we don't perform any kind of division in the loops.
Here's the code:
public ArrayList<Integer> sieve(int n) {
int upperBound = (int) (1.25506 * n / Math.log(n));
ArrayList<Integer> result = new ArrayList<Integer>(upperBound);
if (n >= 2)
result.add(2);
int size = (n - 1) / 2;
BitSet bs = new BitSet(size);
int i = 0;
while (i < size) {
int p = 3 + 2 * i;
result.add(p);
for (int j = i + p; j < size; j += p)
bs.set(j);
i = bs.nextClearBit(i + 1);
}
return result;
}
Restructure your code. Throw out the temporary array, and instead write function that just prime-tests an integer. It will be reasonably fast, since you're only using native types. Then you can, for instance, loop and build a list of integers that are prime, before finally converting that to an array to return.
Not sure if this will suite your situation but you can take a look at my approach. I used mine using Sieve of Eratosthenes.
public static List<Integer> sieves(int n) {
Map<Integer,Boolean> numbers = new LinkedHashMap<>();
List<Integer> primes = new ArrayList<>();
//First generate a list of integers from 2 to 30
for(int i=2; i<n;i++){
numbers.put(i,true);
}
for(int i : numbers.keySet()){
/**
* The first number in the list is 2; cross out every 2nd number in the list after 2 by
* counting up from 2 in increments of 2 (these will be all the multiples of 2 in the list):
*
* The next number in the list after 2 is 3; cross out every 3rd number in the list after 3 by
* counting up from 3 in increments of 3 (these will be all the multiples of 3 in the list):
* The next number not yet crossed out in the list after 5 is 7; the next step would be to cross out every
* 7th number in the list after 7, but they are all already crossed out at this point,
* as these numbers (14, 21, 28) are also multiples of smaller primes because 7 × 7 is greater than 30.
* The numbers not crossed out at this point in the list are all the prime numbers below 30:
*/
if(numbers.get(i)){
for(int j = i+i; j<n; j+=i) {
numbers.put(j,false);
}
}
}
for(int i : numbers.keySet()){
for(int j = i+i; j<n && numbers.get(i); j+=i) {
numbers.put(j,false);
}
}
for(int i : numbers.keySet()){
if(numbers.get(i)) {
primes.add(i);
}
}
return primes;
}
Added comment for each steps that has been illustrated in wikipedia
I have done using HashMap and found it very simple
import java.util.HashMap;
import java.util.Map;
/*Using Algorithms such as sieve of Eratosthanas */
public class PrimeNumber {
public static void main(String[] args) {
int prime = 15;
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
hashMap.put(0, 0);
hashMap.put(1, 0);
for (int i = 2; i <= prime; i++) {
hashMap.put(i, 1);// Assuming all numbers are prime
}
printPrimeNumberEratoshanas(hashMap, prime);
}
private static void printPrimeNumberEratoshanas(HashMap<Integer, Integer> hashMap, int prime) {
System.out.println("Printing prime numbers upto" + prime + ".....");
for (Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
if (entry.getValue().equals(1)) {
System.out.println(entry.getKey());
for (int j = entry.getKey(); j < prime; j++) {
for (int k = j; k * j <= prime; k++) {
hashMap.put(j * k, 0);
}
}
}
}
}
}
Think this is effective
public static void primes(int n) {
boolean[] lista = new boolean[n+1];
for (int i=2;i<lista.length;i++) {
if (lista[i]==false) {
System.out.print(i + " ");
}
for (int j=i+i;j<lista.length;j+=i) {
lista[j]=true;
}
}
}