SockMerchant Challenge Ruby Array#count not counting? - arrays

So, i'm doing a beginners challenge on HackerHank and, a strange behavior of ruby is boggling my mind.
The challenge is: find and count how many pairs there are in the array. (sock pairs)
Here's my code.
n = 100
ar = %w(50 49 38 49 78 36 25 96 10 67 78 58 98 8 53 1 4 7 29 6 59 93 74 3 67 47 12 85 84 40 81 85 89 70 33 66 6 9 13 67 75 42 24 73 49 28 25 5 86 53 10 44 45 35 47 11 81 10 47 16 49 79 52 89 100 36 6 57 96 18 23 71 11 99 95 12 78 19 16 64 23 77 7 19 11 5 81 43 14 27 11 63 57 62 3 56 50 9 13 45)
def sockMerchant(n, ar)
counter = 0
ar.each do |item|
if ar.count(item) >= 2
counter += ar.count(item)/2
ar.delete(item)
end
end
counter
end
print sockMerchant(n, ar)
The problem is, it doesn't count well. after running the function, in it's internal array ar still have countable pairs, and i prove it by running it again.
There's more. If you sort the array, it behaves differently.
it doesnt make sense to me.
you can check the behavior on this link
https://repl.it/repls/HuskyFrighteningNaturallanguage

You're deleting items from a collection while iterating over it - expect bad stuff to happen. In short, don't do that if you don't want to have such problems, see:
> arr = [1,2,1]
# => [1, 2, 1]
> arr.each {|x| puts x; arr.delete(x) }
# 1
# => [2]
We never get the 2 in our iteration.
A simple solution, that is a small variation of your code, could look as follows:
def sock_merchant(ar)
ar.uniq.sum do |item|
ar.count(item) / 2
end
end
Which is basically finding all unique socks, and then counting pairs for each of them.
Note that its complexity is n^2 as for each unique element n of the array, you have to go through the whole array in order to find all elements that are equal to n.
An alternative, first group all socks, then check how many pairs of each type we have:
ar.group_by(&:itself).sum { |k,v| v.size / 2 }
As ar.group_by(&:itself), short for ar.group_by { |x| x.itself } will loop through the array and create a hash looking like this:
{"50"=>["50", "50"], "49"=>["49", "49", "49", "49"], "38"=>["38"], ...}
And by calling sum on it, we'll iterate over it, summing the number of found elements (/2).

Related

2D matrix to 3D matrix with row to [row, col] mapping

I have a 2D matrix with in the 1st dimension different channels, and in the 2nd dimension time samples. I want to rearrange this to a 3D matrix, with in the 1st and 2nd dimension channels, and in the 3rd time samples.
The channels have to mapped according to a certain mapping. Right now I am using a for-loop to do so, but what would be a no-loop solution?
N_samples = 1000;
N_channels = 64;
channel_mapping = reshape(1:64, [8 8]).';
% Results in mapping: (can also be random)
% 1 2 3 4 5 6 7 8
% 9 10 11 12 13 14 15 16
% 17 18 19 20 21 22 23 24
% 25 26 27 28 29 30 31 32
% 33 34 35 36 37 38 39 40
% 41 42 43 44 45 46 47 48
% 49 50 51 52 53 55 55 56
% 57 58 59 60 61 62 63 64
data = rand(N_channels, N_samples);
data_grid = NaN(8,8, N_samples);
for k = 1:N_samples
tmp = data(:, k);
data_grid(:, :, k) = tmp(channel_mapping);
end
You can do it in one go as follows:
data_grid = reshape(data(channel_mapping, :), 8, 8, []);

Retrieving specific elements in matlab from arrays in MATLAB

I have an array in MATLAB
For example
a = 1:100;
I want to select the first 4 element in every successive 10 elements.
In this example I want to b will be
b = [1,2,3,4,11,12,13,14, ...]
can I do it without for loop?
I read in the internet that i can select the element for each step:
b = a(1:10:end);
but this is not working for me.
Can you help me?
With reshape
%// reshaping your matrix to nx10 so that it has successive 10 elements in each row
temp = reshape(a,10,[]).'; %//'
%// taking first 4 columns and reshaping them back to a row vector
b = reshape(temp(:,1:4).',1,[]); %//'
Sample Run for smaller size (although this works for your actual dimensions)
a = 1:20;
>> b
b =
1 2 3 4 11 12 13 14
To vectorize the operation you must generate the indices you wish to extract:
a = 1:100;
b = a(reshape(bsxfun(#plus,(1:4)',0:10:length(a)-1),[],1));
Let's break down how this works. First, the bsxfun function. This performs a function, here it is addition (#plus) on each element of a vector. Since you want elements 1:4 we make this one dimension and the other dimension increases by tens. this will lead a Nx4 matrix where N is the number of groups of 4 we wish to extract.
The reshape function simply vectorizes this matrix so that we can use it to index the vector a. To better understand this line, try taking a look at the output of each function.
Sample Output:
>> b = a(reshape(bsxfun(#plus,(1:4)',0:10:length(a)-1),[],1))
b =
Columns 1 through 19
1 2 3 4 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43
Columns 20 through 38
44 51 52 53 54 61 62 63 64 71 72 73 74 81 82 83 84 91 92
Columns 39 through 40
93 94

Multiple array items appending

This is the reproducible code:
a <- rep(1, 20)
a[c(1, 12, 15)] <- 0
b <- which(a == 0)
set.seed(123)
d <- round(runif(17) * 100)
I would like to append 0s to d to get the following result:
[1] 0 29 79 41 88 94 5 53 89 55 46 0 96 45 0 68 57 10 90 25
that is equal to d after appending 0s to each element which has index equal to b - 1.
I've seen append() accepts just one single "after" value, not more than one.
How could I do?
Please, keep in mind I cannot change the length of d because it is supposed it's the ouput of a quite long function, not a simple random sequence like in this example.
You can use negative subscripting to assign the non-zero elements to a new vector.
D <- numeric(length(c(b,d)))
D[-b] <- d
D
# [1] 0 29 79 41 88 94 5 53 89 55 46 0 96 45 0 68 57 10 90 25

Bash random number generator where number is not in array

I am attempting to create a random number generator that generates a number between 1 and 99 but not any number that has already been generated.
In the script array1 contains the numbers already generated. To make it easier to test I have reduced the random number range to 0 - 14 and manually created an array.
I am quite new to bash scripting and am picking it up with a couple of books and the internet.
I have tried a mixture of ideas, the one that seems to make most sense is
array1=( 1 2 3 6 7 8 9 10 11 12 13 )
func1() {
for var in "${array1[#]}"
do
echo $var
done
}
rnd=$[ $RANDOM % 14 ]
until [ $rnd != func1 ]
do
rnd=$[ $RANDOM % 14 ]
done
echo $rnd
however I know the problem is on line 9 the shell sees the following code:
until [ $rnd != 1 2 3 6 7 8 9 10 11 12 13 ]
I know that the solution is that line 9 needs to be:
until [ $rnd != 1 ] && [ $rnd != 2 ] && [ $rnd != 3 ] && ...
I just don't know how to make this happen automatically from the array. The array does vary in length depending on how many numbers have been generated.
Any help will be greatly appreciated!
This is something that I found difficulty doing in bash. The approach I came up with is to have func1() return true or false and modify the array to remove the number that has been picked.
array=( {1..15} )
func1() {
local pick="$1"
found=1
total=${#array[#]}
for ((i=0;i<total;i++)); do
if (( pick == ${array[i]} )); then
echo $pick
array=( ${array[#]:0:i} ${array[#]:((i + 1)):$total})
found=0
break
fi
done
return $found
}
numbers=3
for ((x=0;x<numbers;x++)); do
until func1 $(( $RANDOM % ( ${#array[#]} ) )); do
continue
done
done
As noted in one of the comments, using the Knuth Shuffle is an excellent way to do this
#!/bin/bash
shuffle() {
local i tmp size max rand
# Code from http://mywiki.wooledge.org/BashFAQ/026
# $RANDOM % (i+1) is biased because of the limited range of $RANDOM
# Compensate by using a range which is a multiple of the array size.
size=${#array[*]}
max=$(( 32768 / size * size ))
for ((i=size-1; i>0; i--)); do
while (( (rand=$RANDOM) >= max )); do :; done
rand=$(( rand % (i+1) ))
tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
done
}
# Fill an array with values 1 to 99
array=({1..99});
# Shuffle the array at random
shuffle
# Echo shuffled array
echo ${array[#]}
Output
$ ./knuth
58 78 6 37 84 79 81 43 50 25 49 56 99 41 26 15 86 11 96 90 76 46 92 70 87 27 33 91 1 2 73 97 65 69 42 32 39 67 72 52 36 64 24 88 60 35 83 89 66 30 4 53 57 28 75 48 40 74 18 23 45 61 20 31 21 16 68 80 62 8 98 14 7 19 47 55 22 85 59 17 77 10 63 93 51 54 95 82 94 9 44 38 13 71 34 29 5 3 12
You can also use the -R switch to sort, if your version of sort supports it:
for x in {1..99} ; do echo $x ; done | sort -R

How can i create an array and auto populate the elements in perl

lets say i have an array :
#time = qw(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
);
but the values 1..50 depend on the size of an array #arr
so instead of declaring #time manually, how can i populate #time with 1 .. #arr, and possibly have other TYPES of elements like TIME in seconds, etc.
This will initialise #time with the values from 1 to $#arr:
#time = (1..$#arr);
I suspect you probably want 0 .. $#arr rather than 1 .. $#arr?
and possibly have other TYPES of elements like TIME in seconds, etc.
I'm not quite sure what you mean here, but you should have a look at map for one convenient way of generating a list of values by transforming another list. That might be what you're after.
#time = 1 .. #arr;
If you want to do something with each number, like multiply them by 2, you can use map:
#time = map { 2 * $_ } 1 .. #arr;

Resources