Finding 'distance' between elements in an array - java

I'm trying to solve an auto-cipher and to find the key length i need to find the 'distance' between all elements in the cipher-text.
I've found the distances between all elements in the array however i now want some way to find the frequency of each jump.
So for example, if i had a string "ababbababba" and wanted to work with a's, the frequency of jumps of 1 is 2 and the frequency of jumps of 2 is 2.
for(int i = 1; i<cipher2.length(); i++ ){
if(cipher2Array[i] == 'f') {
arrayList.add(i);
int jumpDistance = arrayList.get(i) - arrayList.get(i-1);
}
}
So basically from here, with my jumpDistance variable, how would I something like
if(jumpDistance == theSameinAnyOtherPlaceOfArray) {
counter++;
}
And output a type of table with jumpSize, frequency

You would need a HashMap<Integer, Integer>. You can store the jumpSize as a key and the corresponding frequency as value.
Yet again if you want multiple characters to be eveluated at one go, you would need a nested Map ie - a map for each character Map<Character, Map<Integer, Integer>> as suggested by #SAM.
Map<Integer, Integer> counter = new HashMap<Integer, Integer>();
for(int i = 1; i<cipher2.length(); i++ ){
if(cipher2Array[i] == 'f') {
arrayList.add(i);
int jumpDistance = arrayList.get(i) - arrayList.get(i-1);
Integer freq = counter.get(jumpdistance);
freq = freq == null ? 1 : freq+1;
counter.put(jumpDistance, freq);
}
}

Related

Java HashMap wont insert duplications

I am trying to put count of characters into hashMap but it wont insert.
for(int i = 0; i < N; i++) {
str1.put(s.charAt(i),
str1.getOrDefault(str1.get(s.charAt(i)), 0) + 1);
str2.put(t.charAt(i),
str2.getOrDefault(str2.get(t.charAt(i)) ,0) + 1);
}
Thanks.
The correct pattern is:
str1.put(s.charAt(i), 1 + str1.getOrDefault(s.charAt(i), 0));
Your current code is doing a lookup, by key, of the current value mapped to the character, which initially will be null.
Instead of explicitly fetching the value to increment the count, I would use compute.
basically, if v is null, it initializes to 1. Otherwise it takes the current value and adds 1.
String str = "Programming is fun";
Map<Character, Integer> map = new HashMap<>();
for(int i = 0; i < str.length(); i++) {
map.compute(str.charAt(i), (k,v)-> v == null ? 1 : v + 1);
}
map.entrySet().forEach(System.out::println);
Prints
=2
P=1
a=1
r=2
s=1
u=1
f=1
g=2
i=2
m=2
n=2
o=1
And you can also do a frequency count using streams.
Map<Character, Long> map = Arrays.stream(str.split("")).
collect(Collectors.groupingBy(s->s.charAt(0),
Collectors.counting()));

Java: How to iterate over an ArrayList without using nested loops to insert key-value pair in HashMap

List<Integer> list = new ArrayList<>();
Map<Integer, Integer> map = new HashMap<>();
int key = 1, value = 1, counter = 0;
for(value = 1; value <= list.size(); ++value) {
counter = list.get(value-1);
while(counter != 0) {
map.put(key, value);
++key;
--counter;
}
}
This is a nested looping solution I could come up with. I want to iterate over each element of ArrayList and run inner loop till the integer value becomes zero for every element of ArrayList, and insert key and value in HashMap.
I want to achieve this using a single loop only because for higher integer values this seems like a inefficient solution.
Thank You.

How would I set the max amount of times an int can appear from a random number generator?

Say if I randomly generated 30 numbers from 1 to 50 but for example I didn't want 4 to occur more than 3 times at most, or 23 to occur more than once. How would I go about doing this?
I would use a Map. If you haven't used a Map before, check this link out.
Then you could implement something like this:
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for(...)
{
//#Generate Random Number#//
Integer occur = occurrences.get(randNum);
occurrences.put(randNum, (occur == null) ? 1 : occur + 1);
if(occurrences.get(randNum) > occurrenceLimit)
{
//#Generate a different random number and try again#//
}
}
Here is one possible solution using pseudocode
//initialize a list
Collection numbers;
for i = some min value; i < desired max number; i++
numbers.add(i)
Integer grabRandomNumber()
{
//now generate a random INDEX based on the size of your collections list
index = randomNumber(0, numbers.size() - 1)
//Now go to that index and grab that number
numberYouWant = numbers.get(index)
//BUT you should remove the number you retrieved so the next time
//you try and grab a number you won't grab a duplicate
numbers.remove(index)
//then just return the number you got
return numberYouWant
}
Random rand = new Random();
List<int> randoms = new ArrayList<>();
for (int i = 0 ; i < 30 ; i++) {
int num = rand.nextInt(50) + 1;
if (num == 23 && randoms.contains(23)) {
i--;
break;
}
if (num == 4 && Collections.frequency(randoms, 4) >= 3) {
i--;
break;
}
randoms.add(num);
}
Method 1
(most basic, but inefficient)
You can use an integer array with the size of the bounds you are using for your integers (or a Hashanah with Integer to Integer for large ranges) and every time a number appears you increment the integer value.
Then when looping you check if the returned value has exceeded it's limit of uses and get another value and repeat.
Method 2
There is a second method which can be better than the first as when there is only one value possible out of 100 numbers the method could repeat many times before one result.
What you do is use a
HashMap of Integer to Integer.
You then put all possible values as keys into it with their limits of returns (and a flag for no limit) as the values.
You then generate a random number from 0 to the HashMap size - 1 and get the corresponding index in the keySet if the HashMap and decrement the value related to the key until it is 0 and then you remove the value.
Example:
HashMap map = new HashMap();
Random r = new Random();
while( map.size() > 0){
int index =r.nextInt (map.size());
int key = map.keySet().get(index);
map.put(key, map.get(key)-1);
if( map.get(key) == 0){
map.remove(key);
}
}
First of all, putting restrictions on this kind of thing somewhat defeats the whole definition of random.
That said, you can count the number of times each number is generated and check the count against whatever rules you need before returning the latest figure.

Find unique items from two arrays

I was wondering what could be a better solution that could produce less complexity than O(n^2) when printing unique items from two arrays. Any ideas?
int[] a = {1,2,4,5,8};
int[] b = {3,2,5,7,8};
ArrayList unMatch = new ArrayList() ;
for(int i=0; i<a.length; i++){
boolean contains = false;
innerloop:
for(int k =0; k<b.length; k++){
if(a[i]==b[k]){
contains = true;
break innerloop;
}
}
if(!contains){
unMatch.add(a[i]);
}
}
for(int i=0; i<b.length; i++){
boolean contains = false;
innerloop:
for(int k =0; k<a.length; k++){
if(b[i]==a[k]){
contains = true;
break innerloop;
}
}
if(!contains){
unMatch.add(b[i]);
}
}
Output: [1,4,3,7]
I think this sort of solution will be better, if you can use other data structures:
First we will fill up a HashMap<Integer, Integer> with the items and their frequencies:
public static Set<Entry<Integer, Integer>> fillMap(int[] a, int[] b) {
HashMap<Integer, Integer> entries = new HashMap<>();
for (Integer i : a)
entries.put(i, entries.get(i) == null ? 1 : entries.get(i) + 1);
for (Integer i : b)
entries.put(i, entries.get(i) == null ? 1 : entries.get(i) + 1);
return entries.entrySet();
}
And then print the unique items (the ones with value = 1):
for (Entry<Integer, Integer> entry: fillMap(a, b))
if (entry.getValue() == 1)
System.out.println("This value is unique: " + entry.getKey() );
If I'm not mistaken this should run in O(n+m) (or just O(n) if the arrays are the same length always).
convert array to array list
List<Integer> c = Array.asList(a);
List<Integer> d = Array.asList<b>;
c.removeAll(d);
c.addAll(d);
c.froEach(System.out::println);
I did this in java using lambdas it is only O(n)
Hope this code answers your question
Use of Set can reduce your complexity. Sets don't allow duplicates. Sets can be:
HashSet - HashSet is implemented using a hash table. Elements are not ordered. The add, remove, and contains methods have constant time complexity O(1).
LinkedHashSet - uses Trees (RB-Trees). Elements are not ordered. Complexity for the same methods is O(log n)
TreeSet - uses a hash table with a linked list running through it. Elements are ordered. The time complexity of the same methods is O(1).
E.g.
HashSet<Integer> set = new HashSet<>();
for(int n : a) {
set.add(n);
}
for (int n : b) {
set.add(n);
}
So, it provides a linear order here - O(n+m).

Get a least occurred value in a Map

i have no idea how to get the value in a map that occurred the least.
problem :
Write a method rarest that accepts a map whose keys are strings and whose values are integers as a parameter and returns the integer value that occurs the fewest times in the map. If there is a tie, return the smaller integer value. If the map is empty, throw an exception.
For example, suppose the map contains mappings from students' names
(strings) to their ages (integers). Your method would return the least
frequently occurring age. Consider a map variable m containing the
following key/value pairs:
{Alyssa=22, Char=25, Dan=25, Jeff=20, Kasey=20, Kim=20, Mogran=25,
Ryan=25, Stef=22} Three people are age 20 (Jeff, Kasey, and Kim), two
people are age 22 (Alyssa and Stef), and four people are age 25 (Char,
Dan, Mogran, and Ryan). So a call of rarest(m) returns 22 because only
two people are that age.
If there is a tie (two or more rarest ages
that occur the same number of times), return the youngest age among
them. For example, if we added another pair of Kelly=22 to the map
above, there would now be a tie of three people of age 20 (Jeff,
Kasey, Kim) and three people of age 22 (Alyssa, Kelly, Stef). So a
call of rarest(m) would now return 20 because 20 is the smaller of the
rarest values.
now i believe that this code give me the smallest int count but how do i get that value?
public static int rarest (Map<String, Integer> map) {
    List<Integer> list = new ArrayList<Integer>();
    for(Integer i: map.values()) {
        list.add(i);
    }
    int min = 0, count = 0;
    for(Integer i: list) {
        count = Collections.frequency(list, i);
if(count < min) {
min = count;
}
    }
    return min;  
}
Keep track of the value of i which corresponds to the lowest count min. This should look familiar:
public static int rarest (Map<String, Integer> map) {
List<Integer> list = new ArrayList<Integer>();
for(Integer i: map.values()) {
list.add(i);
}
int min = Integer.MAX_VALUE, rarestValue = 0;
for(Integer i: list) {
int count = Collections.frequency(list, i);
if(count < min || (count == min && i < rarestValue)) {
min = count;
rarestValue = i;
}
}
return rarestValue;
}
do it like this,
public static int rarest (Map<String, Integer> map) {
List<Integer> list = new ArrayList<>(map.values());
Collections.sort(list); // you need to sort the list first
int min = list.get(0); // this is your bug, min shouldn't start at 0
int count = 0, rarest = 0;
for(Integer i: list) {
count = Collections.frequency(list, i);
if(count < min) {
min = count;
rarest = i;
}
}
return rarest;
}
Your bug is that min shouldn't be initialized at 0 but at the first value of the list
If you don't sort the list first, in case of a tie, you won't necessarily get the youngest

Categories

Resources