Related
I'm confused with the USACO Cowpatibility solution explanation and code.
The problem is defined here: http://usaco.org/index.php?page=viewproblem2&cpid=862.
Their solution is defined here: http://usaco.org/current/data/sol_cowpatibility_gold_dec18.html.
I know their linear solution requires the property of inclusion and exclusion (PIE), and I understand this property, but I am confused about how they implemented it. I'm looking for an explanation of these lines:
"This motivates the following inclusion-exclusion solution: for every subset of flavors, count how many pairs of cows that like all flavors within each subset. We add all the counts for subsets of size 1, then to avoid double-counting, we subtract all the counts for subsets of size 2. We then add all the counts of subsets of size 3, subtract all the counts of subsets of size 4, and add the counts of subsets of size 5."
How do they determine every possible subset, and what are these subsets? Why are there only 31N subsets? It would also be helpful if someone gives examples of what the subsets would be for their sample case.
They generated and stored subsets in order to keep track of the number of pairs of cows with 1 flavor in common, 2 flavors in common, 3 flavors in common, 4 flavors in common, and all 5 flavors in common. To do this, they used a map.
Now, there are 31N subsets because for each cow, you can create 31 combinations of favorite flavors. For example, Cow 1's favorite flavors of ice cream were 1, 2, 3, 4, 5. So the different subsets were:
{1, 0, 0, 0, 0} {1, 3, 0, 0, 0} {2, 5, 0, 0, 0} {1, 2, 5, 0, 0}
{1, 2, 0, 0, 0} {1, 4, 0, 0, 0} {1, 3, 4, 0, 0} {2, 3, 5, 0, 0}
{1, 2, 3, 0, 0} {1, 5, 0, 0, 0} {1, 3, 5, 0, 0} {3, 4, 5, 0, 0}
{1, 2, 3, 4, 0} {2, 3, 0, 0, 0} {2, 3, 4, 0, 0} {1, 2, 3, 5, 0}
{1, 2, 3, 4, 5} {2, 4, 0, 0, 0} {2, 4, 5, 0, 0} {1, 3, 4, 5, 0}
{2, 3, 4, 5, 0} {2, 0, 0, 0, 0} {3, 0, 0, 0, 0} {4, 0, 0, 0, 0}
{5, 0, 0, 0, 0} {3, 4, 0, 0, 0} {3, 5, 0, 0, 0} {4, 5, 0, 0, 0}
{1, 4, 5, 0, 0} {1, 2, 4, 0, 0} {1, 2, 4, 5, 0}
As you can see, there are 31 subsets. (This is because there are 2^5 = 32 sets that can be made, including an empty set. 32 - 1 = 31.) Since N ≤ 50,000, you can generate 31N subsets. After scanning through the input, the code generated the subsets for each cow and added them to a map:
map<S5, int> subsets;
They mapped each combination to the number of times it was seen. Some examples of entries for the sample input would be:
{
[{1, 0, 0, 0, 0}, 2], # 2 cows, Cow 1 and Cow 2 both like flavor 1
[{8, 10, 0, 0, 0}, 2], # 2 cows, Cow 2 and Cow 3 both like flavors 8 and 10
[{50, 60, 80, 0, 0}, 1], # 1 cow, Cow 4 liked flavors 50, 60, 80
# and so on...
}
Finally, based the number of nonzero numbers in the subset, the algorithm applies the inclusion-exclusion principle. It simply iterates through all 31N subsets, and either adds or subtracts the count stored in the map for that subset. (If it was 1, 3, or 5 nonzero numbers the counts were added; else they were subtracted.) It then subtracts this answer from N * (N-1) / 2 to output the number of pairs of cows that aren't compatible.
I hope this explanation helps! Good luck for future contests!
There are 31N distinct subsets because each cow has five possible flavor choices. Specifically, this line explains the subsets:
we can explicitly generate all the subsets of flavors where at least
one cow likes all the flavors in that subset
The way to do that is to iterate over all N cows then construct the power set of flavors that they like, excluding the empty set. There are 2^5 sets in the power set, so removing the empty set results in 31. Therefore, there are 31N sets total.
An example is quite helpful here, taking the sample input:
4
1 2 3 4 5 # Cow 0
1 2 3 10 8 # Cow 1
10 9 8 7 6 # Cow 2
50 60 70 80 90 # Cow 3
The subsets will be:
{
{1}, {1, 2}, {1, 3}, ..., {2, 3, 4, 5}, {1, 2, 3, 4, 5}, # Cow 0
{1}, {1, 2}, {1, 3}, ..., {2, 3, 10, 8}, {1, 2, 3, 10, 8}, # Cow 1
...
}
Each cow generates 31 subsets. From there, the algorithm counts the number of cows that generate a specific subset (for example, note that {1} is generated by both cow 0 and 1, we just keep track of how many cows generate each subset), and applies inclusion-exclusion based on the subset size.
Nice problem, I used to do USACO and they had really interesting problems which still stand out amongst the "clever" interview questions a lot of companies give. :)
I am modifying the perimeter values on a matrix, then trying to recurse into the inner values. I expected I'd be able to access the inner values with something like matrix[1:3][1:3]. This is not the case, and I'm a bit lost as to the underlying logic of how Go handles sequential brackets.
package main
import (
"fmt"
)
var m = [][]int{
[]int{0, 1, 2, 3},
[]int{4, 5, 6, 7},
[]int{8, 9, 10, 11},
[]int{12, 13, 14, 15},
}
I am trying to access the values 5, 6, 9, 10 in the above matrix -- the "inner" matrix.
func main() {
inner := m[1:3][1:3]
fmt.Printf("%#v\n", inner)
// Expected Output: [][]int{
// []int{5, 6},
// []int{9, 10}
// }
// Actual Ouput: [][]int{
// []int{8, 9, 10, 11},
// []int{12, 13, 14, 15}
// }
inner = m[1:3]
fmt.Printf("%#v\n", inner)
// Output:
// [][]int{
// []int{4, 5, 6, 7},
// []int{8, 9, 10, 11}
// }
inner = innerMatrix(m)
fmt.Printf("%#v\n", inner)
// [][]int{
// []int{5, 6},
// []int{9, 10}
}
func innerMatrix(m [][]int) (inner [][]int) {
innerRows := m[1:3]
for _, row := range innerRows {
inner = append(inner, row[1:3])
}
return
}
The function innerMatrix is able to produce the output I expected. I'm lost as to why (1) m[1:3][1:3] doesn't have the same effect, (2) it seems to translate to m[2:4]. What's going on?
Playground
When you create a sub-slice in Go, you're allowed to grow that sub-slice back to the capacity of the original slice again, e.g.:
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
b := a[1:3]
fmt.Printf("a[1:3]: %v\n", b)
c := b[1:3]
fmt.Printf("b[1:3]: %v\n", c)
}
which outputs:
a[1:3]: [2 3]
b[1:3]: [3 4]
Note that b only has two elements, but we're allowed to create a slice of the second and third elements because the capacity of the slice it's a sub-slice of is sufficiently large, and all the slices share the same underlying array. See the last example in section "Slice internals" on this page
So what's happening in your case is that m[1:3] is equivalent to:
var m1 = [][]int{
[]int{4, 5, 6, 7}, // second element of m
[]int{8, 9, 10, 11}, // third element of m
}
and m[1:3][1:3] is therefore equivalent to m1[1:3], which is equivalent to:
var m2 = [][]int{
[]int{8, 9, 10, 11}, // second element of m1
[]int{12, 13, 14, 15}, // "third" element of m1
}
with the "third" element appearing only because the capacity of m is large enough to contain it, and does in fact contain it. If m only had three elements, this would cause a panic.
In other words, m[1:3][1:3] is here exactly equivalent to m[2:4], because m[1:3][1:3] gives you the second and third elements of m[1:3]. Maybe easier to understand with a diagram:
m : []int{1, 2, 3, 4}
m[1:3] : []int{ 2, 3 }
m[1:3][1:3] : []int{ 3, 4}
m[2:4] : []int{ 3, 4}
As an oversimplification, you can imagine that the square brackets give the requested elements of whatever is immediately to the left of them, so a somewhat extreme example:
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
b := a[1:5]
fmt.Printf("b: %v, a[1:5] : %v\n",
b, a[1:5])
c := b[1:4]
fmt.Printf("c: %v , a[1:5][1:4] : %v\n",
c, a[1:5][1:4])
d := c[1:3]
fmt.Printf("d: %v , a[1:5][1:4][1:3] : %v\n",
d, a[1:5][1:4][1:3])
e := d[1:2]
fmt.Printf("e: %v , a[1:5][1:4][1:3][1:2]: %v\n",
e, a[1:5][1:4][1:3][1:2])
}
which outputs:
b: [2 3 4 5], a[1:5] : [2 3 4 5]
c: [3 4 5] , a[1:5][1:4] : [3 4 5]
d: [4 5] , a[1:5][1:4][1:3] : [4 5]
e: [5] , a[1:5][1:4][1:3][1:2]: [5]
Here is your data:
var m = [][]int{
[]int{0, 1, 2, 3},
[]int{4, 5, 6, 7},
[]int{8, 9, 10, 11},
[]int{12, 13, 14, 15},
}
First you ask what is inner := m[1:3][1:3]?
Well, taking one at a time, m[1:3] is the subslice you get when grabbing elements 1 to 3 not including 3. So that is elements 1 and 2. Element 0 is []int{0, 1, 2, 3}, element 1 is []int{4, 5, 6, 7}, and element 2 is []int{8, 9, 10, 11},
So m[1:3] is
[][]int{
[]int{4, 5, 6, 7},
[]int{8, 9, 10, 11},
}
Now, to get m[1:3][1:3] we repeat the same thing on this result.
However, this time, there are only two elements. We again want elements 1 and 2. We skip element 0 which is []int{4, 5, 6, 7}. Element 1 is []int{8, 9, 10, 11}. There is no element 2. However, the result of m[1:3] was a subslice and the underlying slice had an additional element.
So by extending this subslice we can get that element back again, and that is the hiddent element 2 which is []int{12, 13, 14, 15}.
So m[1:3][1:3] is
[][]int{
[]int{8, 9, 10, 11},
[[]int{12, 13, 14, 15},
}
Finally, you wonder why func innerMatrix works differently? It starts by producing m[1:3] which is
[][]int{
[]int{4, 5, 6, 7},
[]int{8, 9, 10, 11},
}
Then the code IS NOT taking another subslice in the same way that m[1:3][1:3] is two consecutive subslices. Instead, you take each element in the slice and grab the subslice from each element. The first time through you grab subslices [1:3] from []int{4, 5, 6, 7}. The result of that is
[]int{5, 6}
The second time through you do the same on the second element []int{8, 9, 10, 11} which is
[]int{9, 10}
Finally, each time through you append the result to the result slice, so you append []int{5, 6} to [], to give [][]{int{5, 6}}, and then you append []int{9, 10} to give
[][]{int{5, 6}, []int{9, 10} }
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Given an array of 0 and 1, e.g. array[] = {0, 1, 0, 0, 0, 1, ...}, how I can predict what the next value will be with the best possible accuracy?
What kind of methods are best suited for this kind of task?
The prediction method would depend on the interpretation of data.
However, it looks like in this particular case we can make some general assumptions that might justify use of certain machine learning techniques.
Values are generated one after another in chronological order
Values depend on some (possibly non-observable) external state. If the state repeats itself, so do the values.
This is a pretty common scenario in many machine learning contexts. One example is the prediction of stock prices based on history.
Now, to build the predictive model you'll need to define the training data set. Assume our model looks at the last k values. In case if k=1, we might end up with something similar to a Markov chain model.
Our training data set will consist of k-dimensional data points together with their respective dependent values. For example, suppose k=3 and we have the following input data
0,0,1,1,0,1,0,1,1,1,1,0,1,0,0,1...
We'll have the following training data:
(0,0,1) -> 1
(0,1,1) -> 0
(1,1,0) -> 1
(1,0,1) -> 0
(0,1,0) -> 1
(1,0,1) -> 1
(0,1,1) -> 1
(1,1,1) -> 1
(1,1,1) -> 0
(1,1,0) -> 1
(1,0,1) -> 0
(0,1,0) -> 0
(1,0,0) -> 1
Now, let's say you want to predict the next value in the sequence. The last 3 values are 0,0,1, so the model must predict the value of the function at (0,0,1), based on the training data.
A popular and relatively simple approach would be to use a multivariate linear regression on a k-dimensional data space. Alternatively, consider using a neural network if linear regression underfits the training data set.
You might need to try out different values of k and test against your validation set.
You could use a maximum likelihood estimator for the Bernoulli distribution. In essence you would:
look at all observed values and estimate parameter p
then use p to determine the next value
In Python this could look like this:
#!/usr/bin/env python
from __future__ import division
signal = [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0]
def maximum_likelihood(s, last=None):
"""
The maximum likelihood estimator selects the parameter value which gives
the observed data the largest possible probability.
http://mathworld.wolfram.com/MaximumLikelihood.html
If `last` is given, only use the last `n` values.
"""
if not last:
return sum(s) / len(s)
return sum(s[:-last]) / last
if __name__ == '__main__':
hits = []
print('p\tpredicted\tcorrect\tsignal')
print('-\t---------\t-------\t------')
for i in range(1, len(signal) - 1):
p = maximum_likelihood(signal[:i]) # p = maximum_likelihood(signal[:i], last=2)
prediction = int(p >= 0.5)
hits.append(prediction == signal[i])
print('%0.3f\t%s\t\t%s\t%s' % (
p, prediction, prediction == signal[i], signal[:i]))
print('accuracy: %0.3f' % (sum(hits) / len(hits)))
The output would like this:
# p predicted correct signal
# - --------- ------- ------
# 1.000 1 False [1]
# 0.500 1 True [1, 0]
# 0.667 1 True [1, 0, 1]
# 0.750 1 False [1, 0, 1, 1]
# 0.600 1 False [1, 0, 1, 1, 0]
# 0.500 1 True [1, 0, 1, 1, 0, 0]
# 0.571 1 False [1, 0, 1, 1, 0, 0, 1]
# 0.500 1 True [1, 0, 1, 1, 0, 0, 1, 0]
# 0.556 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1]
# 0.600 1 False [1, 0, 1, 1, 0, 0, 1, 0, 1, 1]
# 0.545 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0]
# 0.583 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1]
# 0.615 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]
# 0.643 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1]
# 0.667 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
# 0.688 1 False [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1]
# 0.647 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0]
# 0.667 1 False [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1]
# 0.632 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0]
# 0.650 1 True [1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1]
# accuracy: 0.650
You could vary the window size for performance reasons or to favor recent events.
In above example, if we would estimate the the next value by looking only at the last 3 observed values, we could increase our accuracy to 0.7.
Update: Inspired by Narek's answer I added a logistic regression classifier example to the gist.
You can predict by calculating the probabilities of 0s and 1s and make their probability ranges and then draw a random number between 0 and 1 to predict.....
If these are series of numbers that are generated each time after some reset event, and next numbers are somehow related to previous ones, you could create a tree (binary tree with two branches at each node in your case) and feed in such historical series from the root, adjusting weights (say a count) on each branch you follow.
Could divide such counts by the number of series you entered before using them, or keep a number on each node too, increased before choosing a branch. That way root node contains number of series entered.
Then, as you feed it a new sequence you can see which branch is "hotter" (would make nice visualization as heatmap/tree btw) to follow, especially if sequence is long enough. That is, assuming order of items in sequence plays a role in what comes next.
I have the following problem.
I need to build a very large number of definitions(*) such as
f[{1,0,0,0}] = 1
f[{0,1,0,0}] = 2
f[{0,0,1,0}] = 3
f[{0,0,0,1}] = 2
...
f[{2,3,1,2}] = 4
...
f[{n1,n2,n3,n4}] = some integer
...
This is just an example. The length of the argument list does not need to be 4 but can be anything.
I realized that the lookup for each value slows down with exponential complexity when the length of the argument list increases. Perhaps this is not so strange, since it is clear that in principle there is a combinatorial explosion in how many definitions Mathematica needs to store.
Though, I have expected Mathematica to be smart and that value extract should be constant time complexity. Apparently it is not.
Is there any way to speed up lookup time? This probably has to do with how Mathematica internally handles symbol definition lookups. Does it phrases the list until it finds the match? It seems that it does so.
All suggestions highly appreciated.
With best regards
Zoran
(*) I am working on a stochastic simulation software that generates all configurations of a system and needs to store how many times each configuration occurred. In that sense a list {n1, n2, ..., nT} describes a particular configuration of the system saying that there are n1 particles of type 1, n2 particles of type 2, ..., nT particles of type T. There can be exponentially many such configurations.
Could you give some detail on how you worked out that lookup time is exponential?
If it is indeed exponential, perhaps you could speed things up by using Hash on your keys (configurations), then storing key-value pairs in a list like {{key1,value1},{key2,value2}}, kept sorted by key and then using binary search (which should be log time). This should be very quick to code up but not optimum in terms of speed.
If that's not fast enough, one could think about setting up a proper hashtable implementation (which I thought was what the f[{0,1,0,1}]=3 approach did, without having checked).
But some toy example of the slowdown would be useful to proceed further...
EDIT: I just tried
test[length_] := Block[{f},
Do[
f[RandomInteger[{0, 10}, 100]] = RandomInteger[0, 10];,
{i, 1, length}
];
f[{0, 0, 0, 0, 1, 7, 0, 3, 7, 8, 0, 4, 5, 8, 0, 8, 6, 7, 7, 0, 1, 6,
3, 9, 6, 9, 2, 7, 2, 8, 1, 1, 8, 4, 0, 5, 2, 9, 9, 10, 6, 3, 6,
8, 10, 0, 7, 1, 2, 8, 4, 4, 9, 5, 1, 10, 4, 1, 1, 3, 0, 3, 6, 5,
4, 0, 9, 5, 4, 6, 9, 6, 10, 6, 2, 4, 9, 2, 9, 8, 10, 0, 8, 4, 9,
5, 5, 9, 7, 2, 7, 4, 0, 2, 0, 10, 2, 4, 10, 1}] // timeIt
]
with timeIt defined to accurately time even short runs as follows:
timeIt::usage = "timeIt[expr] gives the time taken to execute expr,
repeating as many times as necessary to achieve a total time of \
1s";
SetAttributes[timeIt, HoldAll]
timeIt[expr_] := Module[{t = Timing[expr;][[1]], tries = 1},
While[t < 1.,
tries *= 2;
t = Timing[Do[expr, {tries}];][[1]];
];
Return[t/tries]]
and then
out = {#, test[#]} & /# {10, 100, 1000, 10000, 100000, 100000};
ListLogLogPlot#out
(also for larger runs). So it seems constant time here.
Suppose you enter your information not like
f[{1,0,0,0}] = 1
f[{0,1,0,0}] = 2
but into a n1 x n2 x n3 x n4 matrix m like
m[[2,1,1,1]] = 1
m[[1,2,1,1]] = 2
etc.
(you could even enter values not as f[{1,0,0,0}]=1, but as f[{1,0,0,0},1] with
f[li_List, i_Integer] := Part[m, Apply[Sequence, li + 1]] = i;
f[li_List] := Part[m, Apply[Sequence, li + 1]];
where you have to initialize m e.g. by m = ConstantArray[0, {4, 4, 4, 4}];)
Let's compare timings:
testf[z_] :=
(
Do[ f[{n1, n2, n3, n4}] = RandomInteger[{1,100}], {n1,z}, {n2,z}, {n3,z},{n4,z}];
First[ Timing[ Do[ f[{n2, n4, n1, n3}], {n1, z}, {n2, z}, {n3, z}, {n4, z} ] ] ]
);
Framed[
ListLinePlot[
Table[{z, testf[z]}, {z, 22, 36, 2}],
PlotLabel -> Row[{"DownValue approach: ",
Round[MemoryInUse[]/1024.^2],
" MB needed"
}],
AxesLabel -> {"n1,n2,n3,n4", "time/s"},ImageSize -> 500
]
]
Clear[f];
testf2[z_] :=
(
m = RandomInteger[{1, 100}, {z, z, z, z}];
f2[ni__Integer] := m[[Sequence ## ({ni} + 1)]];
First[ Timing[ Do[ f2[{n2, n4, n1, n3}], {n1, z}, {n2, z}, {n3, z}, {n4, z}] ] ]
)
Framed[
ListLinePlot[
Table[{z, testf2[z]}, {z, 22, 36, 2}],
PlotLabel -> Row[{"Matrix approach: ",
Round[MemoryInUse[]/1024.^2],
" MB needed"
}],
AxesLabel -> {"n1,n2,n3,n4", "time/s"}, ImageSize -> 500
]
]
gives
So for larger sets up information a matrix approach seems clearly preferrable.
Of course, if you have truly large data, say more GB than you have RAM, then you just
have to use a database and DatabaseLink.
Given a list of integers like {2,1,1,0} I'd like to list all permutations of that list that are not equivalent under given group. For instance, using symmetry of the square, the result would be {{2, 1, 1, 0}, {2, 1, 0, 1}}.
Approach below (Mathematica 8) generates all permutations, then weeds out the equivalent ones. I can't use it because I can't afford to generate all permutations, is there a more efficient way?
Update: actually, the bottleneck is in DeleteCases. The following list {2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0} has about a million permutations and takes 0.1 seconds to compute. Apparently there are supposed to be 1292 orderings after removing symmetries, but my approach doesn't finish in 10 minutes
removeEquivalent[{}] := {};
removeEquivalent[list_] := (
Sow[First[list]];
equivalents = Permute[First[list], #] & /# GroupElements[group];
DeleteCases[list, Alternatives ## equivalents]
);
nonequivalentPermutations[list_] := (
reaped = Reap#FixedPoint[removeEquivalent, Permutations#list];
reaped[[2, 1]]
);
group = DihedralGroup[4];
nonequivalentPermutations[{2, 1, 1, 0}]
What's wrong with:
nonequivalentPermutations[list_,group_]:= Union[Permute[list,#]& /# GroupElements[group];
nonequivalentPermutations[{2,1,1,0},DihedralGroup[4]]
I don't have Mathematica 8, so I can't test this. I just have Mathematica 7.
I got an elegant and fast solution from Maxim Rytin, relying on ConnectedComponents function
Module[{gens, verts, edges},
gens = PermutationList /# GroupGenerators#DihedralGroup[16];
verts =
Permutations#{2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0};
edges = Join ## (Transpose#{verts, verts[[All, #]]} &) /# gens;
Length#ConnectedComponents#Graph[Rule ### Union#edges]] // Timing