Knapsack problem/Continuous/J: Difference between revisions

From Rosetta Code
Content added Content deleted
(Created page with "Here's a look at the intermediate values developed in this solution, since they may shed some light on the approach used here. First, we use J's parser (which considers seque...")
 
m (Fixed syntax highlighting.)
 
Line 3: Line 3:
First, we use J's parser (which considers sequences of numbers separated by spaces to be a single token) to split the raw text into two variables:
First, we use J's parser (which considers sequences of numbers separated by spaces to be a single token) to split the raw text into two variables:


<lang J> names
<syntaxhighlight lang="j"> names
beef
beef
pork
pork
Line 22: Line 22:
3.7 67
3.7 67
3.0 95
3.0 95
5.9 98</lang>
5.9 98</syntaxhighlight>


Then we convert the text in <code>numbers</code> to real numbers, and put each column in a different variable.
Then we convert the text in <code>numbers</code> to real numbers, and put each column in a different variable.


<lang J> weights
<syntaxhighlight lang="j"> weights
3.8 5.4 3.6 2.4 4 2.5 3.7 3 5.9
3.8 5.4 3.6 2.4 4 2.5 3.7 3 5.9
prices
prices
36 43 90 45 30 56 67 95 98</lang>
36 43 90 45 30 56 67 95 98</syntaxhighlight>


Next, we compute price-to-weight ratio and find the indices which would sort this in decreasing order.
Next, we compute price-to-weight ratio and find the indices which would sort this in decreasing order.


<lang J> prices%weights
<syntaxhighlight lang="j"> prices%weights
9.47368 7.96296 25 18.75 7.5 22.4 18.1081 31.6667 16.6102
9.47368 7.96296 25 18.75 7.5 22.4 18.1081 31.6667 16.6102
order
order
7 2 5 3 6 8 0 1 4</lang>
7 2 5 3 6 8 0 1 4</syntaxhighlight>


Next, we use this order to pick out the weights
Next, we use this order to pick out the weights


<lang J> order{weights
<syntaxhighlight lang="j"> order{weights
3 3.6 2.5 2.4 3.7 5.9 3.8 5.4 4</lang>
3 3.6 2.5 2.4 3.7 5.9 3.8 5.4 4</syntaxhighlight>


and find their running sum
and find their running sum


<lang J> +/\ order{weights
<syntaxhighlight lang="j"> +/\ order{weights
3 6.6 9.1 11.5 15.2 21.1 24.9 30.3 34.3</lang>
3 6.6 9.1 11.5 15.2 21.1 24.9 30.3 34.3</syntaxhighlight>


And we clip that running sum at 15
And we clip that running sum at 15


<lang> 15 <. +/\ order{weights
<syntaxhighlight lang="j"> 15 <. +/\ order{weights
3 6.6 9.1 11.5 15 15 15 15 15</lang>
3 6.6 9.1 11.5 15 15 15 15 15</syntaxhighlight>


Except let's decorate that algorithm with punctuation, to make it a single verb, in preparation to the next step:
Except let's decorate that algorithm with punctuation, to make it a single verb, in preparation to the next step:


<lang> 15&<.&(+/\) order{weights
<syntaxhighlight lang="j"> 15&<.&(+/\) order{weights
3 6.6 9.1 11.5 15 15 15 15 15</lang>
3 6.6 9.1 11.5 15 15 15 15 15</syntaxhighlight>


Here we use J's [[j:Vocabulary/ampdot|under]] to perform the inverse of running sum on that result:
Here we use J's [[j:Vocabulary/ampdot|under]] to perform the inverse of running sum on that result:


<lang J> 15&<.&.(+/\) order{weights
<syntaxhighlight lang="j"> 15&<.&.(+/\) order{weights
3 3.6 2.5 2.4 3.5 0 0 0 0</lang>
3 3.6 2.5 2.4 3.5 0 0 0 0</syntaxhighlight>


This under mechanism is a feature of the language, and is quite often super convenient, but can be a bit difficult to explain to users of languages which have nothing comparable.
This under mechanism is a feature of the language, and is quite often super convenient, but can be a bit difficult to explain to users of languages which have nothing comparable.
Line 69: Line 69:
... and that's the value we stuck in <code>take</code>
... and that's the value we stuck in <code>take</code>


<lang J> take
<syntaxhighlight lang="j"> take
3 3.6 2.5 2.4 3.5 0 0 0 0</lang>
3 3.6 2.5 2.4 3.5 0 0 0 0</syntaxhighlight>


Note that since all weights are positive, we can find the non-empty weights by checking their sign (which we will use in a moment):
Note that since all weights are positive, we can find the non-empty weights by checking their sign (which we will use in a moment):


<lang J> *take
<syntaxhighlight lang="j"> *take
1 1 1 1 1 0 0 0 0</lang>
1 1 1 1 1 0 0 0 0</syntaxhighlight>


Now we form the result into a column:
Now we form the result into a column:


<syntaxhighlight lang="j">
<lang J>
,.take
,.take
3
3
Line 89: Line 89:
0
0
0
0
0</lang>
0</syntaxhighlight>


Format as text and prepend a space:
Format as text and prepend a space:


<lang J> ' ',.":,.take
<syntaxhighlight lang="j"> ' ',.":,.take
3
3
3.6
3.6
Line 102: Line 102:
0
0
0
0
0</lang>
0</syntaxhighlight>


Then prepend the names (which we must sort using the order we came up with earlier, so the names match up with the numbers):
Then prepend the names (which we must sort using the order we came up with earlier, so the names match up with the numbers):


<lang J> (order{names),.' ',.":,.take
<syntaxhighlight lang="j"> (order{names),.' ',.":,.take
salami 3
salami 3
ham 3.6
ham 3.6
Line 115: Line 115:
beef 0
beef 0
pork 0
pork 0
flitch 0</lang>
flitch 0</syntaxhighlight>


And, finally, we strip off the rows which are zeros (by taking one copy of each of the rows where we are taking something and zero copies of the rows where we are not):
And, finally, we strip off the rows which are zeros (by taking one copy of each of the rows where we are taking something and zero copies of the rows where we are not):


<lang J> (*take)#(order{names),.' ',.":,.take
<syntaxhighlight lang="j"> (*take)#(order{names),.' ',.":,.take
salami 3
salami 3
ham 3.6
ham 3.6
brawn 2.5
brawn 2.5
greaves 2.4
greaves 2.4
welt 3.5</lang>
welt 3.5</syntaxhighlight>


And we are done!
And we are done!
Line 130: Line 130:
Well, almost... to find the total price, we first sort the weights we are taking of each item in ascending order of the ordering indices:
Well, almost... to find the total price, we first sort the weights we are taking of each item in ascending order of the ordering indices:


<lang J> take/:order
<syntaxhighlight lang="j"> take/:order
0 0 3.6 2.4 0 2.5 3.5 3 0</lang>
0 0 3.6 2.4 0 2.5 3.5 3 0</syntaxhighlight>


This puts them in the same order as the original weights. So now we can divide by those weights to find how much of each we are taking:
This puts them in the same order as the original weights. So now we can divide by those weights to find how much of each we are taking:


<lang J> (take/:order) % weights
<syntaxhighlight lang="j"> (take/:order) % weights
0 0 1 1 0 1 0.945946 1 0</lang>
0 0 1 1 0 1 0.945946 1 0</syntaxhighlight>


Mostly all or nothing, except for the item we sliced up.
Mostly all or nothing, except for the item we sliced up.
Line 142: Line 142:
And now we can multiply our prices by that to find the price for each item we are taking:
And now we can multiply our prices by that to find the price for each item we are taking:


<lang J> prices*(take/:order) % weights
<syntaxhighlight lang="j"> prices*(take/:order) % weights
0 0 90 45 0 56 63.3784 95 0</lang>
0 0 90 45 0 56 63.3784 95 0</syntaxhighlight>


And we just have to add those up to get the total price:
And we just have to add those up to get the total price:


<lang J> +/prices*(take/:order) % weights
<syntaxhighlight lang="j"> +/prices*(take/:order) % weights
349.378</lang>
349.378</syntaxhighlight>


And all of this relates to how one normally codes in J - you take small steps, checking your work at each step to make sure it makes sense.
And all of this relates to how one normally codes in J - you take small steps, checking your work at each step to make sure it makes sense.

Latest revision as of 22:11, 30 August 2022

Here's a look at the intermediate values developed in this solution, since they may shed some light on the approach used here.

First, we use J's parser (which considers sequences of numbers separated by spaces to be a single token) to split the raw text into two variables:

   names
beef   
pork   
ham    
greaves
flitch 
brawn  
welt   
salami 
sausage
   numbers
3.8  36
5.4  43
3.6  90
2.4  45
4.0  30
2.5  56
3.7  67
3.0  95
5.9  98

Then we convert the text in numbers to real numbers, and put each column in a different variable.

   weights
3.8 5.4 3.6 2.4 4 2.5 3.7 3 5.9
   prices
36 43 90 45 30 56 67 95 98

Next, we compute price-to-weight ratio and find the indices which would sort this in decreasing order.

   prices%weights
9.47368 7.96296 25 18.75 7.5 22.4 18.1081 31.6667 16.6102
   order
7 2 5 3 6 8 0 1 4

Next, we use this order to pick out the weights

   order{weights
3 3.6 2.5 2.4 3.7 5.9 3.8 5.4 4

and find their running sum

   +/\ order{weights
3 6.6 9.1 11.5 15.2 21.1 24.9 30.3 34.3

And we clip that running sum at 15

   15 <. +/\ order{weights
3 6.6 9.1 11.5 15 15 15 15 15

Except let's decorate that algorithm with punctuation, to make it a single verb, in preparation to the next step:

   15&<.&(+/\) order{weights
3 6.6 9.1 11.5 15 15 15 15 15

Here we use J's under to perform the inverse of running sum on that result:

   15&<.&.(+/\) order{weights
3 3.6 2.5 2.4 3.5 0 0 0 0

This under mechanism is a feature of the language, and is quite often super convenient, but can be a bit difficult to explain to users of languages which have nothing comparable.

(For the record, though, the inverse of running sum is the first value followed by the pairwise differences of adjacent values in the list - J has a library of inverse mechanisms which it brings to bear here, and allows the programmer to specify their own inverse for user defined verbs - which can be abused, but should not be abused since there's no point to such abuse. Still, J's reference works take pains to call this "obverse" instead of "inverse" to allow for cases where true inverse is not possible.)

... and that's the value we stuck in take

   take
3 3.6 2.5 2.4 3.5 0 0 0 0

Note that since all weights are positive, we can find the non-empty weights by checking their sign (which we will use in a moment):

   *take
1 1 1 1 1 0 0 0 0

Now we form the result into a column:

   ,.take
  3
3.6
2.5
2.4
3.5
  0
  0
  0
  0

Format as text and prepend a space:

   ' ',.":,.take
   3
 3.6
 2.5
 2.4
 3.5
   0
   0
   0
   0

Then prepend the names (which we must sort using the order we came up with earlier, so the names match up with the numbers):

   (order{names),.' ',.":,.take
salami    3
ham     3.6
brawn   2.5
greaves 2.4
welt    3.5
sausage   0
beef      0
pork      0
flitch    0

And, finally, we strip off the rows which are zeros (by taking one copy of each of the rows where we are taking something and zero copies of the rows where we are not):

   (*take)#(order{names),.' ',.":,.take
salami    3
ham     3.6
brawn   2.5
greaves 2.4
welt    3.5

And we are done!

Well, almost... to find the total price, we first sort the weights we are taking of each item in ascending order of the ordering indices:

   take/:order
0 0 3.6 2.4 0 2.5 3.5 3 0

This puts them in the same order as the original weights. So now we can divide by those weights to find how much of each we are taking:

   (take/:order) % weights
0 0 1 1 0 1 0.945946 1 0

Mostly all or nothing, except for the item we sliced up.

And now we can multiply our prices by that to find the price for each item we are taking:

   prices*(take/:order) % weights
0 0 90 45 0 56 63.3784 95 0

And we just have to add those up to get the total price:

   +/prices*(take/:order) % weights
349.378

And all of this relates to how one normally codes in J - you take small steps, checking your work at each step to make sure it makes sense.

Similarly, to read J, the easiest way is often to ignore the code and examine representative data. And once you understand what's happening with the data you can go look up any operation you weren't understanding.

That said, note that there are some frequent idioms (such as the techniques used here with sorting) which have broad utility. (Current SQL standards explicitly avoid this utility due to reasoning which if - if it had been followed consistently - would prohibit use of nulls and would have an implicit DISTINCT on all selects.)