How can I get around a slurpy parameter in the Perl 6 signature? - signature

I have this short example where I want to take out two elements of an Array and replace it with a single element that's an array:
my #digits = <1 2 3>.map: { [ $_, $_ ] };
say #digits; # [[1 1] [2 2] [3 3]]
#digits.splice: 0, 2, [4,4];
say #digits; # [4 4 [3 3]]
This doesn't work because the splice replacement is actually the slurpy *#replacement, so it flattens it all.
I could try to itemize it so it's treated as a single thing, but that is still flattened:
#digits.splice: 0, 2, $[4,4];
say #digits;
I can put that item in another array and I get what I want, but this seems too much work (especially since I'd like to decide if it's flattened or not):
#digits.splice: 0, 2, [$[4,4]];
say #digits; # [[4 4] [3 3]]
Is there a better way to work around this?

You can use ([4,4],) (as alternative for [$[4,4]]
#digits.splice: 0, 2, ([4,4],); #or [[4,4],];
or more replacements
#digits.splice: 0, 2, [4,4], Empty; #or ,slip();

Related

Obtain sum of subsets using recursion

I am working on a code to identify subsets of an array with sum of the elements in subsets equal to a given number, k. Here is the recursive code I attempted.
First approach:
def subset_sum(arr,curr,k,idx):
if idx==len(arr):
print(curr) //print the current subset for debugging
if sum(curr)==k:
print(curr)
return
subset_sum(arr,curr,k,idx+1) //exclude the element
curr.append(arr[idx]) //include the element
subset_sum(arr,curr,k,idx+1) // make recursive call
Then this function has been called using;subset_sum([1,2,3],[],4,0), and we get following output;
[]
[3]
[3]
[3, 2]
[3, 2, 3]
[3, 2, 3, 1]
[3, 2, 3, 1, 3]
[3, 2, 3, 1, 3, 2]
[3, 2, 3, 1, 3, 2, 3]
It is being difficult to comprehend, why are the elements being duplicated above. After some brainstorming, I tried second approach below.
Second approach.
def subset_sum(arr,curr,k,idx):
cnt=0
if idx==len(arr):
if sum(curr)==k:
cnt+=1
print(curr,cnt)
return cnt
subset_sum(arr,curr,k,idx+1)
subset_sum(arr,curr+[arr[idx]],k,idx+1)
We call the function subset_sum([1,2,3],[],3,0,0). This gives the output as;
[1,2],1
[3],1
since 1+2 gives us the required sum 3. May I know what is wrong in updating the element curr as I did in the first approach; curr.append(arr[idx]). And why is that the second approach is working fine.

How to numpy sum take range repeat

How to numpy sum take range repeatedly?
For example from below array.
array([[1,2],
[3,4],
[5,6],
[7,8],
...
[i,j]])
I would like to below after sum take two from column 1 and mean take two from column 2 repeatedly.
[4,3] = [sum(1,3), mean(2,4)]...
array([[4,3],
[12,7],
...
Here is a trick to do your job:
b = np.zeros((int(a.shape[0]/2), *a.shape[1:]))
b[:, 0] = np.sum(a.reshape(-1, 2, 2), axis=1).reshape(-1, 2)[:, 0]
b[:, 1] = np.mean(a.reshape(-1, 2, 2), axis=1).reshape(-1, 2)[:, 1]
Which basically reshapes to chunk data into every two rows with a.reshape(-1, 2, 2), then do any calculations you would like, and finally reshape it back to original shape by reshape(-1, 2). You can probably optimize it if efficiency matters for you by removing extra column calculations of num/mean.
EDIT: You can convert your datatype to int if that is a requirement:
b = b.astype(int)
output for array:
[[ 1 2]
[ 3 4]
[ 5 6]
[ 7 8]
[ 9 10]
[11 12]]
is:
[[ 4 3]
[12 7]
[20 11]]
UPDATE: In case of general range.
Range is number of rows you want to bundle together and this answer assumes number of rows in data is multiple of range. i.e. the array is broadcast able to shape(-1,3,2):
range = 3
b = np.zeros((int(a.shape[0]/range), *a.shape[1:]))
b[:, 0] = np.sum(a.reshape(-1, range, 2), axis=1).reshape(-1, 2)[:, 0]
b[:, 1] = np.mean(a.reshape(-1, range, 2), axis=1).reshape(-1, 2)[:, 1]
Output for range 3:
[[ 9. 4.]
[27. 10.]]
You can do the following:
np.vstack((my_array[::2, 0] + my_array[1::2, 0], (my_array[::2, 1] + my_array[1::2, 1]) / 2)).T
It is all numpy indexing really. my_array[::2, 0] will be the array:
[1, 5, ...]
while my_array[1::2, 0] is the array:
[3, 7, ...]
Hence, you can add them to get the first column of your resulting array. Note that this results in an array with shape (n, ). You can do the same for computing the mean, but you have to change 0 by 1 to get the second column.
Once you get your two arrays, you can stack them vertically using vstack, and finally taking the transpose with the attribute T to get the right shape.
I'm not sure this is the more efficient way of doing this though.

Dynamic programming: How can I break this down in subproblems?

I'm stuck in this problem and I would need some help:
Given an array arr, in each step, 1, 2 or 5 units have to be incremented to all but one item of the array (same amount of units to all of them). The goal is to find the minimum number of steps to all items to be equal.
First example
arr = [1, 1, 5]
1) [1 (+2), 1 (+2), 5] = [3, 3, 5]
2) [3 (+2), 3 (+2), 5] = [5, 5, 5]
Solution: 2 steps
Second example
arr = [2, 2, 3, 7]
1) [2 (+1), 2 (+1), 3, 7 (+1)] = [3, 3, 3, 8]
2) [3 (+5), 3 (+5), 3 (+5), 8] = [8, 8, 8, 8]
Solution: 2 steps
I have tried some things but I'm really stuck.
I consider a base case when all items are already equal. In another case, I try to find all the possible solutions by incrementing 1, 2 and 5 to every item but one in the array:
def equal(arr):
if (allElementsIdentical(arr)):
return 0
min = sys.maxsize
for i in [1, 2, 5]:
for j in range(len(arr)):
#Increment all items by "i", except item at index "j"
newArr = arr.copy()
for k in range(j):
newArr[k] += i
for k in range(j + 1, len(newArr)):
newArr[k] += i
movements = 1 + equal(newArr)
if movements < min:
min = movements
return min
This solution doesn't work because recursion never ends. E.g.
[1, 1, 5] -> [1, 2, 6] -> [1, 3, 7] -> [1, 4, 8] -> [1, 5, 9] -> ...
Is it my initial approach correct? How can I break it down in subproblems properly? How can I get the recurrence relation?
(I'm learning Python, so any comment about the syntax is also appreciated)
To me adding 1, 2 or 5 to all but one element seems a lot easier to think about as subtracting 1, 2 or 5 from just one element.
[1, 1, 5] -> 5 - 2 -> 3 - 2
[2, 2, 3, 7] -> 3 - 1 -> 7 - 5
To construct a recurrence, we can use a histogram and consider that to shift any value would cost its frequency in operations. Since we are allowed to reduce by 1, we can easily set a lower-bound for the lowest target we might need to shift all values to. Since the lowest could be reached by any other value, shifting all values down to (lowest-5) (as the HackerRank editorial notes), would involve n more operations than shifting all elements down to the lowest, as we first shift all elements to the lowest, then apply (-5) to each one.
Also noted by the editorial is that the smallest number of operations, k, to shift x to target 0, can be found in O(1) by the greedy
k = x / 5 + (x % 5) / 2 + (x % 5) % 2
Since you've asked to rather try and form a recurrence, under these circumstances, we would be left with solving the coin change problem (coins [5, 2, 1]) for each value in the histogram to reach the target. These are independent: it makes no difference the order by which we apply coin_change to each value to find the number of operations needed, which we then multiply by the value's frequency. Tallying the total operations for all values to reach each target, we choose the least.
We want to replace this problem with one that produces the same answer but will be easier to evaluate.
The trick to making it easier to evaluate with a dynamic programming approach is to have the same results show up in lots of places. And therefore we have to replace equivalent versions of this problem with normalized ones.
For a start, the answer doesn't depend on the order that the elements of the array are in. So we can replace our arrays with arrays sorted from smallest to largest. The operation is now we add x to everything but one, then reorder to canonical form.
Next, the answer doesn't depend on the value of the smallest element. So we can subtract that value from all entries. The operation is now we add x to everything but one, then reorder to canonical form, then subtract the smallest from everything.
This greatly reduces our problem. Enough that a breadth first search has a shot. But we have one more trick to pull. And that trick is that it doesn't matter what order we apply the operations in. And therefore we can apply all of our 5 operations before our 2 operations before our 1 operations. With this trick, we can replace each normalized node with (node, last_operation) and a starting last_operation of 5. The reason why this is a win is that now we have an upper bound for the rest of an A* search. That bound is the current number of steps + sum of ceil(node[i] / last_operation).
And now this can be solved with A* search. Let's do your examples by hand. Using the notation, (total cost, normalized, last_operation, steps).
Example 1: [1, 1, 5]
We normalize to [0, 0, 4] and have a last_operation of 5 and a cost of 0+0+1 = 1. No steps taken. So we start with:
(1, [0, 0, 4], 5)
We take that out, and consider our operations. We get the following for operation 5:
[0, 0, 4] + [5, 5, 0] = [5, 5, 4] => [0, 1, 1] # cost 1 + 0+1+1 = 3
[0, 0, 4] + [5, 0, 5] = [5, 0, 9] => [0, 5, 9] # cost 1 + 0+1+2 = 4
[0, 0, 4] + [0, 5, 5] = [0, 5, 9] => [0, 5, 9] # cost 1 + 0+1+2 = 4 DUP
And for operation 2 we get:
[0, 0, 4] + [2, 2, 0] = [2, 2, 4] => [0, 0, 2] # cost 1 + 0+0+1 = 2
[0, 0, 4] + [2, 0, 2] = [2, 0, 4] => [0, 2, 4] # cost 1 + 0+1+2 = 4
[0, 0, 4] + [0, 2, 2] = [0, 2, 4] => [0, 2, 4] # cost 1 + 0+1+2 = 4 DUP
And for operation 1 we get:
[0, 0, 4] + [1, 1, 0] = [1, 1, 4] => [0, 0, 3] # cost 1 + 0+0+3 = 4
[0, 0, 4] + [1, 0, 1] = [1, 0, 4] => [0, 1, 4] # cost 1 + 0+1+5 = 6
[0, 0, 4] + [0, 1, 1] = [0, 1, 4] => [0, 1, 4] # cost 1 + 0+1+5 = 6 DUP
We stick the 7 non-dups into our priority queue, and the best that comes out looks like this:
(total cost, normalized, last_operation, steps)
( 2, [0,0,2], 2, 1)
We then try operations 2 and 1 on this, and of course find that one of the outcomes is [0, 0, 0] after 2 steps.

Iterating over an numpy.ndarray along an axis

Suppose I have a 3D array arr. I want to iterate over arr in such a way that each iteration yields a vector along z-axis. This can be done but the solution is not generalized. If the arr.shape and the axis along which the vectors have to be obtained are not known or variable then there seems no straight forward way to do this. Can anyone provide a solution to this?
for line in np.nditer(arr, axis=2):
# Perform operation on line
arr = array(
[[[2, 2, 8, 8],
[6, 2, 1, 5],
[4, 5, 1, 4]],
[[7, 4, 7, 4],
[0, 0, 3, 3],
[7, 6, 8, 0]]]
)
Expected output:
[2 2 8 8]
[6 2 1 5]
[4 5 1 4]
[7 4 7 4]
[0 0 3 3]
[7 6 8 0]
In numpy arrays the shape provides you information about # dimensions and # elements in each of the dimensions. with your code we get,
print(arr.shape)
# (2,3,4)
# 3-D array
# along x-axis = 2 elements each
# along y-axis = 3 elements each
# along z-axis = 4 elements each
So, If i want to look at elements along z-axis for all x-axis and y-axis it will look like
for xid in range(arr.shape[0]): # for each x-axis
for yid in range(arr.shape[1]): # for each y-axis
print(arr[xid, yid, :]) # All elements in z-axis
Writing the suggestions of #hpaulj into an answer.
moveaxis seems to be right answer. However apply_along_axis is intuitive and also very easy to use.

Shortcut for all the possible permutation of a set of numbers for m digits

I have been working on finite field. Suppose I have a prime number p=7. So I get a list q=[0,1,2,3,4,5,6]. Now I want all the possible permutation of the elements of set q for 7 places. For example [1,1,1,4,6,3,1] is one of the possible permutation. Is there any inbuilt command in python for doing that? Actually I am working with bigger field where P is 127 (p=127).
Those aren't permutations because elements are repeated, this looks more like a product.
you can use itertools.product on repeated q lists (here for 3 elements):
import itertools
q=[0,1,2] # or q = list(range(3))
for z in itertools.product(*(q,)*len(q)): # using arg unpacking like if it was (q,q,q)
z = list(z) # to convert as list
print(z)
prints:
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 1, 0]
[0, 1, 1]
[0, 1, 2]
...snip...
[2, 2, 0]
[2, 2, 1]
[2, 2, 2]
for p=3 it prints 3**3 = 27 values. If p=127 well... sounds not reasonable.

Resources