java - Transposition Cipher Enumeration - java

Note: I deleted this post earlier because I found the following post but I'm not sure how to apply it to my problem.
I'm working on a transposition cipher decoder. I have already solved the problem of static columns (1,2,3,4 kept in order), but I'm not sure how to create an array of each possible permutation of a length(Given as a parameter), I understand the easiest way is some kind of recursive method, but whilst attempting this I keep getting lost in the endeavour. (I've been coding all day and am quite tired)
Example array would contain:
1,2,3,4,5,6
2,1,3,4,5,6
2,3,1,4,5,6
...

After being very confused for awhile, and trying a few different things, a friend of mine (Not a user here) gave me the following java solution:
public static void main(String[] args) {
Nibba nib = new Nibba();
List<Character> characterSet = new ArrayList<>();
characterSet.add('1');
characterSet.add('2');
characterSet.add('3');
characterSet.add('4');
characterSet.add('5');
characterSet.add('6');
List<String> perms = nib.generatePermutations(characterSet);
// filter only the permutations of length 6
perms = perms.stream().filter(p -> p.length() == characterSet
.size()).collect(Collectors.toList());
for (String s : perms) {
System.out.println(s);
}
System.out.println("Total permutations = " + perms.size());
}
private List<String> generatePermutations(List<Character> characterSet) {
List<String> permutations = new ArrayList<>();
for (int idx = 0; idx < characterSet.size(); idx++) {
char character = characterSet.get(idx);
// Initialise first "permutation"
if (idx == 0) {
permutations.add(String.valueOf(character));
continue;
}
ArrayList<String> oldPerms = new ArrayList<>(permutations);
for (String subPermutation : oldPerms) {
insertPermutations(permutations, subPermutation, character);
}
}
return permutations;
}
/**
* Insert permutations into the given list
*
* #param list the list
* #param str the string
* #param c the character to insert at each point in the string
*/
private void insertPermutations(List<String> list, String str, char c) {
for (int i = 0; i <= str.length(); i++) {
String newStr = str.substring(0, i) + c + str.substring(i);
list.add(newStr);
}
}

Recall that there are n! permutations of n items. The n! can be easily understood in the following way:
1. There are `n` options for choosing the first item.
2. There are `n-1` items remaining from which to choose the second item
...
n-1. There are `2` options left for the `n-1`-th item.
n. There is only 1 item left for the `n`-th position.
Thus there are (n) * (n-1) * (n-2) * ... (2) * (1) = n! total choices for how to order the items.
This directly reveals a method for enumerating the permutations using a mixed-radix numbering scheme. In this scheme, the most-significant digit will be base n, the next-most-significant digit will be base n-1..etc.
You use such a mixed-radix number to select a permutation in the following way:
Use the most significant digit to select an element from the array (note that the first digit ranges from [0, n-1], and there are n elements to select from, so you can use it as the index of the item to select.)
Remove the selected element from the array, record that it's the first element of the permuted array, and compact the remaining elements to the front of the array.
Use the second-most significant digit to select an element from the remaining items (note that the value of this digit ranges from [0, n-2], and there are n-1 digits remaining)
Remove the selected element recording it as the second element in the permuted array
Repeat until all items have been selected.
If we use an array to represent the mixed-radix number in little-endian digit order, then we would have the following:
int mixed_radix[n] = {0};
You increment this mixed-radix number in the following way:
//Increment the least-significant digit
mixed_radix[0]++;
//Ripple overflow toward the most-significant digit
for(i=0; i<n; i++) {
if(mixed_radix[i] > i) {
mixed_radix[i] = 0;
if(i < n-1)mixed_radix[i+1]++;
}
else {
break;
}
}
So we have a way to initialize the mixed-radix number to zero, and then increment it through every possible value, stopping once it wraps back around to zero. (or after n!-1 increments...)
All that said, there's a lovely numerical trick that can make this even simpler: You can unpack that ugly mixed-radix number from an integer in the following way:
We know that there are n! permutations of n items; for any integer val in the range [0, n!-1] the following provides a bijective mapping between the integer value and a mixed-radix number:
int working = val; //val in the range [0, n!-1]
for(j=0 j<n; j++) {
mixed_radix[j] = working % (j+1);
working /= (j+1);
}
So embedding this "unpacking" loop within an outer loop that runs val over the range 0 to n!-1 is a much denser method to enumerate the mixed-radix numbers (which enumerates the possible permutations). It also assigns an integer to each permutation, effectively naming them. :)

Related

After generating an array, how to filter out specific numbers I need?

I have an assignment that I can´t solve for a couple of days. I am very frustrated and feel too dumb for java. The Problem is the extract of the code below (my Professor wrote it). My task is to complete the code. So that when I write in my terminal for example "java Yahtzee 1 1 1 2 3" I should receive "sum of ones: 3".
Why did I stick to this problem? - because I don´t really understand these brackets [i] and how to deal with them.
import java.util.Arrays;
public class Yahtzee {
/**
* ones
* #param dice rolled numbers, sorted in ascending order
* #return the sum of the numbers on all dice showing a 1
*/
private static int aces(int[] dice) {
return -1;
}
public static void main(String[] args) {
// it is allowed to change the main method
if(args.length != 5) {
// the array length should be 5
System.out.println("ERROR: nicht genau 5 Zahlen übergeben");
return;
}
// read the passed numbers
int[] dice = new int[args.length];
for(int i = 0; i < args.length; i++) {
dice[i] = Integer.parseInt(args[i]);
if(dice[i] < 1 || dice[i] > 6) {
System.out.println("Error: invalid number " + dice[i]);
return;
}
}
System.out.println("sum of ones: " + aces(dice));
}
}
I think I should filter out the three ones. My first thought is to make an if-statement in main-method that could look like this: if(dice[i] == 1) and find only those ones. But I dont have idea how to work further. Can some one explain how to solve this problem or give an idea?
I started first programming 1 month ago and learned already some about loops and booleans and arrays. I red other questions like mine, but I do not understand what they do (I only worked with integers and doubles). I think I should work with loops or something like that.
To begin with, it's helpful to know how Yahtzee is played and this link. As you can see in the links, the game of Yahtzee is played with five dice which a player rolls. The value of each dice is held within a int[] Array as an integer element value:
Knowing this and once the dice array is filled, you can iterate through the dice[] Array and sum whatever you like within the aces() method. Obviously, because the method is named aces, it would be any dice[] element that has a value of 1 that is summed, for example:
private static int aces(int[] dice) {
int sum = 0;
/* Read each element of the supplied dice[] Array...
We start `i` at 0 because the first element in any
array is at index 0 (not 1) and we will iterate
through until we reach the array length which is
a literal value. There is no index 5 which is why
it will keep looping for as long as `i` is less
than (<) dice.length. */
for (int i = 0; i < dice.length; i++) {
// Does the current array element contain a value of 1?
if (dice[i] == 1) {
//Yes ... then sum it
sum += dice[i]; // Same as: sum = sum + {value in dice[i] (which is 1)}
}
// No...continue looping.
}
/*
// Can also be done this way with an Enhanced `for` loop:
for (int v : dice) {
if (v == 1) { sum += v; }
}
*/
return sum; // Return the sum of 1's.
}
If you apply the above exampled dice roll to the command-line then the console window should display something like:
Sum of ones: 3
On a side:
When creating your dice[] Array from the command-line arguments (varArgs) you should validate the fact that valid values have been supplied before you add them into the Array. It may not be a big problem in this use-case because the application halts when an invalid value is detected but in the future, this may not always be the situation in another application you develop. Checking for non-numerical (integer) values would be a good idea as well. You application will crash if a letter was supplied in the command-line instead of a integer number. You cover the upper and lower inclusive numerical boundaries but nothing if there is a non-numerical value. This can be solved with a little change to your code:
int[] dice = new int[args.length];
for(int i = 0; i < args.length; i++) {
// If the command-line argument is anything other than 1 to 6...
if(!args[i].matches("[1-6]")) {
System.out.println("Error: Invalid value (" + dice[i]
+ ") in Command-Line arguments!");
return;
}
dice[i] = Integer.parseInt(args[i]);
}
You will notice that the String#matches() method is used for the condition of the if statement and passed to it is a small Regular Expression (regex) of "[1-6]". What this expression does is it will see if the value passed to the current (args[i]) argument it's checking is nothing more than a string representation of a integer numerical value consisting of a inclusive value from 1 to 6. Anything else and the validation fails. You will also note that the NOT operator (!) is used at the beginning of the condition. This would be the same as saying,
"If args[i] does not contain a string representation of a integer value
inclusively between 1 and 6 then, inform the User of an Invalid Argument
and shut down the application, otherwise, add it to the `dice[]` Array."

Java finding all combos in array that add up to specific number [duplicate]

This question already has an answer here:
Finding all the number combos in array that add up to input number
(1 answer)
Closed 6 years ago.
I'm currently working on the following question from a interviewing book:
You are given a random array of 50 unique integers ranging from 1 to 100 inclusive. Write a method using Java that takes in a positive integer as a parameter and returns an array of all the number combinations that add up to that value.
For example, given an array of integers [3,6,1,9,2,5,12] and being passed the integer value 9, you would return [[3,6],[6,1,2],[9],[3,1,5]]. Order of returning the results in the array does not matter, though you should return unique sets (ie. [6,3] and [3,6] are the same and only one should be returned). Also, the individual results should be in the order they are found (ie [6,1,2] should be returned, not [1,2,6]).
I've made decent progress on it, but I fear I may solving this the wrong way.
import java.util.*;
public class findCombinations {
public static void main(String[] args) {
int number;
int[] list = new int[10];
Scanner reader = new Scanner(System.in);
//fill the array
for (int i = 0; i < list.length; i++) {
number = (int)(Math.random() * 10) + 1;
list[i] = number;
for (int j = 0; j < i; j++) { //remove duplicates
if (list[i] == list[j]) {
i--;
break;
}
}
}
Arrays.sort(list);
//test output
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
System.out.println("Enter a number: ");
int input = reader.nextInt();
ArrayList<Integer> trimmedList = new ArrayList<Integer>();
//cut out the numbers that are impossible to use
for (int i = 0; i < list.length; i++) {
if (list[i] <= input) {
trimmedList.add(list[i]);
}
}
//test output
printList(trimmedList);
ArrayList<Integer> comboList = new ArrayList<Integer>();
System.out.println("Finding combinations...");
for (int i = 0; i < trimmedList.size(); i++) {
int current = trimmedList.get(i);
if (current == input) { System.out.println(current); }
else if (current < input) {
comboList.add(current);
if (isCombo(comboList, input)) {
printList(comboList);
}
else { continue; }
}
else { continue; }
}
}
public static boolean isCombo(ArrayList<Integer> list, int input) {
ArrayList<Integer> combo = new ArrayList<Integer>();
int sum = 0;
for (int i : list)
sum += i;
if (sum == input) { return true; }
else { return false; }
}
public static void printList(ArrayList<Integer> list) {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
}
}
}
I know this is incomplete but I wanted to ask if anyone had any suggestions or improvements I could make on this? I sorted my list and trimmed out all the integers that won't possibly be used, but now the hard part is finding all the combos.
There are many different approaches to solve this problem, each with their own merits, so I wouldn't worry too much about whether your answer is the 'right' one or not...so long as it actually solves the problem! Also, an interviewer will likely be more interested in your thought-process, and the strategies you use, rather than a 100% perfect solution written in the span of a few minutes on a whiteboard.
Here's a couple of things to consider:
As you noticed, you can immediately eliminate any integers larger than your target value.
You're essentially generating arbitrarily-sized subsets of your starting array—so Set is likely the most useful data type to work with. {2, 3} and {3, 2} should be seen as identical when you're generating your response set.
Integer partitioning is an NP-Complete problem. It's hard. I think you've taken the correct approach of starting with the array, rather than with the target value.
There are many algorithms for generating combinations of integers from a larger set. Check out this SO answer for a few of them. You can generate k sized combinations from your (already-filtered) starting set, for k from 1-50.
Actually...there are more direct ways to get the power set of your starting set. Consider the inherent structure of a power set (shown below). By enumerating a few examples, you'll notice a natural recurrence in your strategy for identifying the subsets.
As you're generating these combinations, discard any whose elements don't sum to your target value.
Image Source: https://en.wikipedia.org/wiki/Power_set
Since this is a learning exercise, you will benefit most if you can solve this for yourself. So ...
Hints:
Sorting the numbers first is on the right track
I would use recursion to iterate the solutions. Given a partial sum, only numbers less than a certain number are possible candidates to be added to the sum ...
Work out the algorithm in your head >before< you start coding it.
And I agree with what #nbrooks says on the topic of what the interviewers are looking for. You need to be able to think ... and explain your thinking to the interviewer ... at the algorithmic level. That is what will distinguish the excellent candidates from the ordinary ones.
I realize generating your array of random numbers is not part of the problem statement, but I think your difficulties begin here.
First of all, use a Set<Integer> type collection to collect your generated numbers; break when the set reaches the desired size. If generated order is important, use a LinkedHashSet.
Set<Integer> origSet = new HashSet<Integer>(); // fill with random numbers
At some point, you have a list of numbers for which the order matters. Maintain this list as a List<Integer>. The list preserves the order of your original list so that you can produce the number combinations in the right order (i.e., 6 precedes 1, 1 precedes 2).
List<Integer> origList = new ArrayList<Integer>(origSet); // use indexOf method to find index of a number
You create a second list that is sorted; this list is the one used by your recursion algorithm.
List<Integer> sortedList = new ArrayList<Integer>(origList); // sort this
You don't need to trim the list because a recursive algorithm will trim any branch with no feasible solution.
A recursive algorithm can generate the combos in fewer lines of code. Reordering takes a few more lines.

repeated element in Array

I have an array of N elements and contain 1 to (N-1) integers-a sequence of integers starting from 1 to the max number N-1-, meaning that there is only one number is repeated, and I want to write an algorithm that return this repeated element, I have found a solution but it only could work if the array is sorted, which is may not be the case.
?
int i=0;
while(i<A[i])
{
i++
}
int rep = A[i];
I do not know why RC removed his comment but his idea was good.
With the knowledge of N you easy can calculate that the sum of [1:N-1]. then sum up all elementes in your array and subtract the above sum and you have your number.
This comes at the cost of O(n) and is not beatable.
However this only works with the preconditions you mentioned.
A more generic approach would be to sort the array and then simply walk through it. This would be O(n log(n)) and still better than your O(n²).
I you know the maximum number you may create a lookup table and init it with all zeros, walk through the array and check for one and mark the entries with one. The complexity is also just O(n) but at the expense of memory.
if the value range is unknown a simiar approach can be used but instead of using a lookup table a hashset canbe used.
Linear search will help you with complexity O(n):
final int n = ...;
final int a[] = createInput(n); // Expect each a[i] < n && a[i] >= 0
final int b[] = new int[n];
for (int i = 0; i < n; i++)
b[i]++;
for (int i = 0; i < n; i++)
if (b[i] >= 2)
return a[i];
throw new IllegalArgumentException("No duplicates found");
A possible solution is to sum all elements in the array and then to compute the sym of the integers up to N-1. After that subtract the two values and voila - you found your number. This is the solution proposed by vlad_tepesch and it is good, but has a drawback - you may overflow the integer type. To avoid this you can use 64 bit integer.
However I want to propose a slight modification - compute the xor sum of the integers up to N-1(that is compute 1^2^3^...(N-1)) and compute the xor sum of your array(i.e. a0^a1^...aN-1). After that xor the two values and the result will be the repeated element.

Randomly pick k bits out of n from a Java BitSet

How to pick exactly k bits from a Java BitSet of length m with n bits turned on, where k≤n≤m?
Example input: m=20, n=11
Example output: k=3
The naive approach
Choose a random number 0≤ i ≤ m-1.if it's turned on on the input and not turned on on the output, turn it on in the output, until k bits are turned on in the output.
This approach fails when n is much smaller than m. Any other ideas?
You could scan the set from the first bit to the last, and apply reservoir sampling to the bits that are set.
The algorithm has O(m) time complexity, and requires O(k) memory.
How about finding n positions of all set bits and placing them in a collection as the first step, and them choosing k positions from that collection randomly?
If the constraints allow it you can solve the task by:
Construct a List holding all the set bits indexes. Do Collections#shuffle on it. Choose the first k indexes from the shuffled list.
EDIT As per the comments this algorithm can be inefficient if k is really small, whilst n is big. Here is an alternative: generate k random, different numbers in the interval [0, n]. If in the generation of a number the number is already present in the set of chosen indices, do the chaining approach: that is increase the number by 1 until you get a number that is not yet present in the set. Finally the generated indices are those that you choose amongst the set bits.
If n is much larger than k, you can just pare down the Fisher-Yates shuffle algorithm to stop after you've chosen as many as you need:
private static Random rand = new Random();
public static BitSet chooseBits(BitSet b, int k) {
int n = b.cardinality();
int[] indices = new int[n];
// collect indices:
for (int i = 0, j = 0; i < n; i++) {
j=b.nextSetBit(j);
indices[i] =j++;
}
// create returning set:
BitSet ret = new BitSet(b.size());
// choose k bits:
for (int i = 0; i<k; i++) {
//The first n-i elements are still available.
//We choose one:
int pick = rand.nextInt(n-i);
//We add it to our returning set:
ret.set(indices[pick]);
//Then we replace it with the current (n-i)th element
//so that, when i is incremented, the
//first n-i elements are still available:
indices[pick] = indices[n-i-1];
}
return ret;
}

Counting instances of a value in an array

Homework. Dice Game. I've got an array that represents five rolls of a die. Consider:
diceRoll[] = {6,3,3,4,5}. I would LIKE to create a SECOND array that has the counts of values from one to six contained in diceRoll[], (e.g., occurence[] = {0,0,2,1,1,1} for the diceRoll[] above.) but I fear I'm getting lost in nested loops and can't seem to figure out which value I ~should~ be returning. occurence[] is a global variable, and the intent is that the array will contain six values...the count of ones (at index [0]), twos (at [1]), threes (at [2]), etc.
So Far:
for(i=1;i<7;i++) /* die values 1 - 6
{
for(j=0;j<diceRoll.length;j++) /* number of dice
{
if (diceRoll[j] == i) /* increment occurences when die[j] equals 1, then 2, etc.
occurence = occurence + 1;
}
}
return occurence;
}
I cannot, however, get the occurence=occurence+1 to work. bad operand types for binary operator is my most common error. I suspect I need to increment occurence OUTSIDE one or both of the for loops, but I'm lost.
Guidance? or perhaps the one-line easy way to do this?
d
The easiest way I have to do this is to create the second array in order so that
occurrence[0] = # of 1's occurrence[1] = # of 2's and so on. Then this becomes a 1 loop method.
//method to return number of occurrences of the numbers in diceRolls
int[] countOccurrences(int[] diceRolls) {
int occurrence[] = new int[6]; //to hold the counts
for(int i = 0; i < diceRolls.length; i++) { //Loop over the dice rolls array
int value = diceRolls[i]; //Get the value of the next roll
occurence[value]++; //Increment the value in the count array this is equivalent to occurrence[value] = occurrence[value] + 1;
//occurrence[diceRolls[i]]++; I broke this into two lines for explanation purposes
}
return occurrence; //return the counts
}
EDIT:
Then to get the count for any particular value use occurrence[value-1]

Categories