Shuffle a specific number of strings in a String Array in Java - java

I have a long textfile and I need to shuffle all the words which are longer than 4 letters but the other words need to remain in the same place. This without using the Collections module.
I can manage to shuffle all the strings in the array, but I cant figure out for the life of me how to shuffle only a part.
public static String[] getScramble(String text) {
Random rgen=new Random();
String[] textArray=text.split(" ");
for(int i=0;i<textArray.length;i++){
int randPos=rgen.nextInt(textArray.length);
String temp=textArray[i];
if (textArray[i].length()>4){
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}else{
textArray[i]=textArray[i];
}
}
return textArray;
Thanks!

You only swap two words if both are larger than 4 characters, otherwise you keep them in the original position:
for(int i = 0; i < textArray.length; i++) {
int randPos = rgen.nextInt(textArray.length);
if (textArray[i].length() > 4 && textArray[randPos].length() > 4){
String temp = textArray[i];
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}
}
EDIT: if the number of long words is very small compared to the number of short words, this loop may behave badly (since it will fail to swap most of the long words), so you can improve it as follows:
for(int i=0;i<textArray.length;i++){
if (textArray[i].length() > 4) {
// find a long word to swap textArray[i] with
int randPos=rgen.nextInt(textArray.length);
while (textArray[randPos].length() <= 4){
randPos=rgen.nextInt(textArray.length);
}
// swap the two long words
String temp=textArray[i];
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}
}

When generating the random position for a word that is longer than 4 characters, you check to see if the new position also has a word with more than 4 characters. If not, you keep generating a new random position until you find one that works.
public static String[] getScramble(String text) {
Random rgen = new Random();
String[] textArray = text.split(" ");
for (int i = 0; i < textArray.length; i++) {
if( textArray[i].length() > 4) {
String temp = textArray[i];
int randPos = rgen.nextInt(textArray.length);
while( textArray[randPos].length() <= 4 ){
randPos = rgen.nextInt(textArray.length);
}
textArray[i] = textArray[randPos];
textArray[randPos] = temp;
}
}
return textArray;
}
A situation that might arise is if you have only one word with more than 4 characters, which means it's pointless to even try to randomize them, and you might waste a lot of time generating random positions to no avail. To optimize this, you can first check if you have less than 2 long words, and if so you don't need to do anything.
int longWordCount = 0;
for (int i = 0; i < textArray.length; i++) {
if( textArray[i].length() > 4 )
longWordCount++;
if( longWordCount == 2 )
break;
}
if( longWordCount > 1 ) {
for (int i = 0; i < textArray.length; i++) {
if (textArray[i].length() > 4) {
String temp = textArray[i];
int randPos = rgen.nextInt(textArray.length);
while (textArray[randPos].length() <= 4) {
randPos = rgen.nextInt(textArray.length);
}
textArray[i] = textArray[randPos];
textArray[randPos] = temp;
}
}
}

Here's a version that adapts Durstenfeld's algorithm. Note that ThreadLocalRandom is preferable to Random.
public static String[] getScramble(String text) {
ThreadLocalRandom rgen = ThreadLocalRandom.current();
String[] textArray = text.split(" ");
int[] indices = IntStream.range(0, textArray.length)
.filter(i -> textArray[i].length() > 4)
.toArray();
for (int i = indices.length; i > 1; --i) {
int j = indices[rgen.nextInt(i)];
int k = indices[i - 1];
if (j != k) {
String tmp = textArray[j];
textArray[j] = textArray[k];
textArray[k] = tmp;
}
}
return textArray;
}
The OP didn't say that Streams couldn't be used, only Collections. If that's a problem, one can replace the initialization of indices with a simple loop to initialize an array of int of the same size as textArray, using the variable i to keep track of the number of indices entered (so it would be declared and initialized before the main for loop).

Related

Sort digits of a given integer, without '0'

I'm trying to sort the digits of a given integer by turning the integer to a String and creating an array by the size of that String length.
I'm using the modulu option to separate the digits and in the end I'm reversing it by multiplying by 10.
The problem that it's going out of bound each time and I don't know how to make the size of the array to work good for me.
Here are the code :
String s = String.valueOf(num);
int[] arr = new int[s.length()+1];
while(num != 0) {
arr[(int) num % 10]++;
num = num / 10;
}
long result = 0;
for(int i = 0 ; i < arr.length - 1 ; i++){
for(int j = 0 ; j < arr[i] ; j++) {
result = result * 10;
result = result + i;
}
}
return result;
There seems to be a lot of overkill solving this, as in a lot of code, so here is my take on it.
static int sortDigits(int num) {
char[] arr = Integer.toString(num).toCharArray();
Arrays.sort(arr);
return Integer.parseInt(new String(arr));
}
Test
System.out.println(sortDigits(4201514)); // 112445
You can of course do the same for the long and BigInteger versions:
static long sortDigits(long num) {
char[] arr = Long.toString(num).toCharArray();
Arrays.sort(arr);
return Long.parseLong(new String(arr));
}
static BigInteger sortDigits(BigInteger num) {
char[] arr = num.toString().toCharArray();
Arrays.sort(arr);
return new BigInteger(new String(arr));
}
If I understand your question correctly, when given an integer, you want to "sort" each digit, ignoring 0's.
To do so, so you can first convert it to a string:
String value = String.valueOf(num);
Since you have a string, you can use the split() function to split each 'number' like so:
String[] numbers = value.split("");
Then, you can find the indexes of 0's (and store it somewhere).
ArrayList<Integer> indexes = new ArrayList<>();
for (int i = 0; i < numbers.length; i++) {
if (numbers[i].equals("0")) {
indexes.add(i);
}
}
Then, you can sort the array of strings (numbers) using the sort() function:
Arrays.sort(numbers);
Then, you can remove the 0's like this (by creating a new ArrayList):
ArrayList<String> copy = new ArrayList<>();
for (String s : numbers) {
if (!s.equals("0")) {
copy.add(s);
}
}
(Here, you may use ArrayUtils if you already imported the library.)
Then, concatenate each element to make one entire string with join():
String result = String.join("", copy);
Finally, using the indexes, insert 0's to where they were located at initially:
for (int i : indexes) {
result = result.substring(0, i) + "0" + result.substring(i);
}
result would be what you want.
Note: This might not be the most sufficient way to do it, so you can modify it anywhere you want.
Based on your code I made a few modification
size of array should be 10 since in a number only 0 to 9 digits are possible
use this array to make a frequency array of occurrence of each digit
iterate over the array from 1 to 9 and make a sorted number
public static long sortInt(long num) {
int[] arr = new int[10];
while(num != 0) {
arr[(int) num % 10] ++;
num /= 10;
}
long result = 0;
for(int i = 1 ; i < arr.length ; i++)
while(arr[i]-- != 0)
result = result * 10 + i;
return result;
}

Java Sorting Even and Odd Numbers

I'm in a high school programming class right now and my instructions were to create a program that stores the random numbers in an array that's sorted by even and then odd numbers. Here's my code so far, I have no idea why it's not working. It keeps printing out 0's even though I'm supposed to generate numbers above 1. Help would be greatly appreciated, thanks! :D
Random rnd = new Random();
int randomNumArray[] = new int[10];
int evenArray[] = new int[10];
int oddArray[] = new int[10];
int countEven = 0,countOdd = 0;
for (int i = 0; i < randomNumArray.length; i++)
{
randomNumArray[i] = rnd.nextInt(100) + 1;
System.out.println(randomNumArray[i]);
if (randomNumArray[i] % 2 == 0)
{
evenArray[i] = randomNumArray[i];
countEven++;
}
else if (randomNumArray[i] % 2 == 1)
{
oddArray[i] = randomNumArray[i];
countOdd++;
}
}
for (int i = 0; i < countEven; i++)
{
System.out.println(evenArray[i]);
}
for (int i = 0; i < countOdd; i++)
{
System.out.println(oddArray[i]);
}
The problem is you are storing the values in evenArray and oddArray at index i, which is not necessarily the "next unused space" in the array you're writing to. Let's say the first number (when i = 0) is even and the second (when i = 1) is odd; when the second number is stored by oddArray[i] = randomNumArray[i];, it will be stored at index 1, but it is the first odd number so it should be in oddArray at index 0 instead.
To solve this, you can use countEven and countOdd as the correct indices to write to; for example, when countOdd is 0 then there are no odd numbers yet, so index 0 is the correct place to write the first one. So you can change these two lines:
if (randomNumArray[i] % 2 == 0)
{
// changed here
evenArray[countEven] = randomNumArray[i];
countEven++;
}
else if (randomNumArray[i] % 2 == 1)
{
// changed here
oddArray[countOdd] = randomNumArray[i];
countOdd++;
}
You should change these lines
evenArray[countEven] = randomNumArray[i];
oddArray[countOdd] = randomNumArray[i];
... instead of
evenArray[i] = randomNumArray[i];
oddArray[i] = randomNumArray[i];

Terminated due to timeout for my hacker rank solution

Hello all please check the problemHackerRank Problem Statement
This is my solution for the above problem(link)
static int migratoryBirds(List<Integer> arr) {
int ar[]=new int[arr.size()];
for(int i=0;i<arr.size();i++){
ar[i] = Collections.frequency(arr,arr.get(i));
// ar[i] = obj.occuranceOfElement(arr,arr.get(i));
}
int maxAt = 0;
for (int i = 0; i < ar.length; i++) {
maxAt = ar[i] > ar[maxAt] ? i : maxAt;
}
return arr.get(maxAt);
}
my code is unable to handle when the array size is bigger,example 17623 elements in array.
Terminated due to timeout
The problem is in the second for loop which iterates over the array and gives me the index of the largest number in the array.Is there any other way that I could increase the performance.
Your problem is in this part:
for(int i = 0; i < arr.size(); i++)
ar[i] = Collections.frequency(arr, arr.get(i));
This is O(N²): Collections.frequency() iterates over whole list to calculate frequency for only one element. Manually, you can iterate over the list to calculate frequencey for all elements.
Moreover, ther're only 5 birds, so you need only 5 length array.
static int migratoryBirds(int[] arr) {
int max = 1;
int[] freq = new int[6];
for (int val : arr)
freq[val]++;
for (int i = 2; i < freq.length; i++)
max = freq[i] > freq[max] ? i : max;
return max;
}
Your problem is the call to Colletions.frequency, which is an O(N) operation. When you call it from inside a loop it becomes O(N²) and that consumes all your time.
Also, are you sure which implmentation of List you receive? You call list.get(i) which might also be O(N) if the implementation is a LinkedList.
The target of this exercise is to calculate the frequency of each value in one pass over the input. You need a place where you store and increase the number of occurrences for each value and you need to store the largest value of the input.
You have also skipped over a crucial part of the specification. The input has limits which makes solving the problem easier than you now think.
Here's another one:
static int migratoryBirds(List<Integer> arr) {
int freq[]=new int[6];
for(int i=0;i<arr.size();i++){
++freq[arr.get(i)];
}
int maxAt = 1;
for (int i = 2; i < freq.length; i++) {
if (freq[i] > freq[maxAt]) {
maxAt = i;
}
}
return maxAt;
}
We can determine the type number of the most common bird in one loop. This has the time complexity O(n).
static int migratoryBirds(int[] arr) {
int highestFrequency = 0;
int highestFrequencyBirdType = 0;
int[] frequencies = new int[5]; // there are 5 bird types
for (int birdType : arr) {
int frequency = ++frequencies[birdType - 1];
if (frequency > highestFrequency) {
highestFrequency = frequency;
highestFrequencyBirdType = birdType;
} else if (frequency == highestFrequency && birdType < highestFrequencyBirdType) {
highestFrequencyBirdType = birdType;
}
}
return highestFrequencyBirdType;
}
For each element in the array arr we update the overall highestFrequency and are storing the corresponding value representing the highestFrequencyBirdType . If two different bird types have the highest frequency the lower type (with the smallest ID number) is set.

Pseudo-Random sequence generator based on seed

I managed to make a random sequence based on a seed, but now i am trying something else. basically, it generates a number(Based on a seed), then checks if that number is already in the array, then if it isn't in the array, it adds it to the array, and if it is, it goes on to the next one. However, my program works fine until it hits a 0, then it just keeps repeating 0. here is my code:
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Enter a seed(One letter)");
String input = s.nextLine();
char a = input.charAt(0);
int b = ((int) a) - 96;
Random rnd1 = new Random(b);
int[] letters = new int[25];
boolean isSame = false;
for(int i = 0; i < 26; i++)
{
int c = rnd1.nextInt(26);
for(int x = 0; x < i; x++)
{
if(letters[x] == c)
{
isSame = true;
}
}
if(isSame == false)
{
letters[i] = c;
}
System.out.println(letters[i]);
}
}
Break the problem up in to parts, first a method to test for a value in letters between indices 0 and p like
private static boolean match(int[] letters, int p, int val) {
for (int i = 0; i < p; i++) {
if (letters[i] == val) {
return true;
}
}
return false;
}
Then a method to fill an int[] with a provided Random to a given length. Since you want the int[] to be unique, you can use an inner do while loop while the next symbol is already present. Like,
private static int[] fill(Random rnd1, int length) {
int[] letters = new int[length];
for (int i = 0; i < length; i++) {
int c;
do {
c = rnd1.nextInt(length);
} while (match(letters, i, c));
letters[i] = c;
}
return letters;
}
Then you can call that (and you might use Arrays.toString(int[])) to output the array like
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Enter a seed(One letter)");
String input = s.nextLine();
char a = input.charAt(0);
int b = ((int) a) - 96;
Random rnd1 = new Random(b);
int[] letters = fill(rnd1, 26);
System.out.println(Arrays.toString(letters));
}
Two quick fixes:
1) The for loop is going up to index 25 which is ArrayIndexOutOfBoundsException as your array is of length 25.
for(int i = 0; i < 25; i++)
//Your code
}
2) Replace this code with something that can satisfy the condition as well generate a new random number and then assign it to array.
if (letters[x] == c) {
isSame = true;
}
It looks like you want to shuffle the numbers 0-25, that is, storing each number exactly once, but in a random position in the array. This is also known as generating a random permutation. Assuming this is the case, then there are two problems with your code:
isSame needs to be set to false when you pick a new random number, otherwise once a number is repeated, isSame is true for all subsequent numbers and nothing new is written to the array.
When a random number is picked that was already in the array (when isSame is found to be true), a new number needs to be found for that index. In your code, that index is skipped instead, and the value at that index will default to 0.
It is perhaps also worth mentioning that there are more efficient algorithms for this problem. This method will keep picking a lot of random numbers that are then just discarded, which is wasteful. The Fisher-Yates shuffle solves this (pseudocode from wikipedia):
for i from 0 to n−2 do
j ← random integer such that i ≤ j < n
exchange a[i] and a[j]

Java lotto simulation

I am writing a program simulating a lotto draw of six numbers between 1 and 45, a sample output is 3 7 12 27 43 28. But what I am trying to do is count the number of times adjacent numbers appear, for example 1 4 5 29 26 41 is a positive answer because 5 comes after 4.
What is the best way of doing that?
I have tried examples such as :
int adjacent=0;
for(int i =0; i<6; i++)
{
int t = test[i]+1;
test[i]=(int)(45*Math.random())+1;
if(test[i]==t)
adjacent++;
System.out.print(test[i]+" ");
}
This does not work.
What am I doing wrong?
I think you just have an order of operations problem
int adjacent=0;
for(int i =0; i<6; i++)
{
//test[i] hasn't been set yet
int t = test[i]+1;
test[i]=(int)(45*Math.random())+1;
//this comparison doesn't make a whole lot of sense
if(test[i]==t)
adjacent++;
System.out.print(test[i]+" ");
}
Change it around to something like this:
int adjacent=0;
for(int i =0; i<6; i++)
{
test[i]=(int)(45*Math.random())+1;
int t = -1;
//Make sure this comparison only happens after the second iteration
//to avoid index out of bounds
if ( i != 0 )
{
//Set t to the last number + 1 instead of trying to predict the future
t = test[i-1] + 1;
}
//Now this comparison makes a little more sense
//The first iteration will compare to -1 which will always be false
if(test[i]==t)
adjacent++;
System.out.print(test[i]+" ");
}
This can be further simplified to just this:
int adjacent=0;
for(int i =0; i<6; i++)
{
test[i]=(int)(45*Math.random())+1;
if(i != 0 && test[i]==(test[i-1]+1))
adjacent++;
System.out.print(test[i]+" ");
}
After you generate your 6 numbers and put them into an array. Use Arrays.sort(). You can then compare adjacent array entries.
You should also avoid using Random to generate you 6 numbers, because it can generate duplicates. This may or may not accurately simulate your lotto draw. Quoi's answer has a good suggestion for this.
I think you should shuffle it and take any five. Collections#Shuffle would help you, It permutes the specified list using a default source of randomness. All permutations occur with approximately equal likelihood.
List<Integer> list = ArrayList<Integer>();
list.add(1);
list.add(2);
Collections.shuffle(list);
Random rnd = new Random();
Integer[] result = new Integer[5];
result[0] = list.get(rnd.getNextInt(45));
result[1] = list.get(rnd.getNextInt(45));
result[2] = list.get(rnd.getNextInt(45));
result[3] = list.get(rnd.getNextInt(45));
result[4] = list.get(rnd.getNextInt(45));
It always gives you random values, then you should sort it to arrange it in order, say ascending.
Arrays.sort(result);
now you can write a loop to find out adjacent number.
int adjacent = 0;
for(int i=1; i<result.length;i++){
int prev = result[i-1];
int now = result[i];
if(prev+1 == now)
adjacent++;
}
You need to separate the generation of unique (hence the HashSet below to insure identity) random selections, sorting them, and then determining adjacency:
import java.util.HashSet;
import java.util.Arrays;
public class Lotto
{
public Lotto()
{
}
/**
* #param args
*/
public static void main(String[] args)
{
Lotto lotto = new Lotto();
lotto.randomizeSelections(5);
}
private void randomizeSelections(int numOfArrays)
{
for(int i = 0; i < numOfArrays; i++)
{
int[] selArry = new int[6];
//to insure that each random selection is unique
HashSet<Integer> idntySet = new HashSet<Integer>();
for(int j = 0; j < 6;)
{
int rndm = (int)(45 * Math.random()) + 1;
//add selection to the array only if it has not been randomized before
if(!idntySet.contains(rndm))
{
selArry[j] = rndm;
j++;
}
}
//sort the array for determing adjacency
Arrays.sort(selArry);
for(int j = 0; j < 6; j++)
{
int sel = selArry[j];
boolean isAdjcnt = (j > 0 && (sel == selArry[j - 1] + 1)) ? true : false;
System.out.println(i + "." + j + ".random = " + sel);
if(isAdjcnt) System.out.println("\tAdjacent");
}
}
}
}

Categories