mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 23:52:47 +08:00
@@ -6,8 +6,6 @@ package combin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/mat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -185,49 +183,34 @@ func nextCombination(s []int, n, k int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cartesian returns the cartesian product of the slices in data. The Cartesian
|
// Cartesian returns indices into the cartesian product for sets of the given lengths. The Cartesian
|
||||||
// product of two sets is the set of all combinations of the items. For example,
|
// product of two sets is the set of all combinations of the items. For example,
|
||||||
// given the input
|
// given the input
|
||||||
// [][]float64{{1,2},{3,4},{5,6}}
|
// []int{2, 3, 1}
|
||||||
// the returned matrix will be
|
// the returned matrix will be
|
||||||
// [ 1 3 5 ]
|
// [ 0 0 0 ]
|
||||||
// [ 1 3 6 ]
|
// [ 0 1 0 ]
|
||||||
// [ 1 4 5 ]
|
// [ 0 2 0 ]
|
||||||
// [ 1 4 6 ]
|
// [ 1 0 0 ]
|
||||||
// [ 2 3 5 ]
|
// [ 1 1 0 ]
|
||||||
// [ 2 3 6 ]
|
// [ 1 2 0 ]
|
||||||
// [ 2 4 5 ]
|
// Cartesian panics if any of the provided lengths are less than 1.
|
||||||
// [ 2 4 6 ]
|
func Cartesian(lens []int) [][]int {
|
||||||
// If dst is nil, a new matrix will be allocated and returned, otherwise the number
|
if len(lens) == 0 {
|
||||||
// of rows of dst must equal \prod_i len(data[i]), and the number of columns in
|
panic("combin: empty lengths")
|
||||||
// dst must equal len(data). Cartesian also panics if len(data) = 0.
|
|
||||||
func Cartesian(dst *mat.Dense, data [][]float64) *mat.Dense {
|
|
||||||
if len(data) == 0 {
|
|
||||||
panic("combin: empty data input")
|
|
||||||
}
|
}
|
||||||
cols := len(data)
|
|
||||||
rows := 1
|
rows := 1
|
||||||
lens := make([]int, cols)
|
for _, v := range lens {
|
||||||
for i, d := range data {
|
if v < 1 {
|
||||||
v := len(d)
|
panic("combin: length less than zero")
|
||||||
lens[i] = v
|
}
|
||||||
rows *= v
|
rows *= v
|
||||||
}
|
}
|
||||||
if dst == nil {
|
out := make([][]int, rows)
|
||||||
dst = mat.NewDense(rows, cols, nil)
|
|
||||||
}
|
|
||||||
r, c := dst.Dims()
|
|
||||||
if r != rows || c != cols {
|
|
||||||
panic("combin: destination matrix size mismatch")
|
|
||||||
}
|
|
||||||
idxs := make([]int, cols)
|
|
||||||
for i := 0; i < rows; i++ {
|
for i := 0; i < rows; i++ {
|
||||||
SubFor(idxs, i, lens)
|
out[i] = SubFor(nil, i, lens)
|
||||||
for j := 0; j < len(data); j++ {
|
|
||||||
dst.Set(i, j, data[j][idxs[j]])
|
|
||||||
}
|
}
|
||||||
}
|
return out
|
||||||
return dst
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IdxFor converts a multi-dimensional index into a linear index for a
|
// IdxFor converts a multi-dimensional index into a linear index for a
|
||||||
|
@@ -10,7 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/floats"
|
"gonum.org/v1/gonum/floats"
|
||||||
"gonum.org/v1/gonum/mat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// intSosMatch returns true if the two slices of slices are equal.
|
// intSosMatch returns true if the two slices of slices are equal.
|
||||||
@@ -184,62 +183,36 @@ func TestCombinationGenerator(t *testing.T) {
|
|||||||
|
|
||||||
func TestCartesian(t *testing.T) {
|
func TestCartesian(t *testing.T) {
|
||||||
// First, test with a known return.
|
// First, test with a known return.
|
||||||
data := [][]float64{
|
lens := []int{2, 3, 4}
|
||||||
{1, 2},
|
want := [][]int{
|
||||||
{3, 4},
|
{0, 0, 0},
|
||||||
{5, 6},
|
{0, 0, 1},
|
||||||
}
|
{0, 0, 2},
|
||||||
want := mat.NewDense(8, 3, []float64{
|
{0, 0, 3},
|
||||||
1, 3, 5,
|
{0, 1, 0},
|
||||||
1, 3, 6,
|
{0, 1, 1},
|
||||||
1, 4, 5,
|
{0, 1, 2},
|
||||||
1, 4, 6,
|
{0, 1, 3},
|
||||||
2, 3, 5,
|
{0, 2, 0},
|
||||||
2, 3, 6,
|
{0, 2, 1},
|
||||||
2, 4, 5,
|
{0, 2, 2},
|
||||||
2, 4, 6,
|
{0, 2, 3},
|
||||||
})
|
{1, 0, 0},
|
||||||
got := Cartesian(nil, data)
|
{1, 0, 1},
|
||||||
if !mat.Equal(want, got) {
|
{1, 0, 2},
|
||||||
t.Errorf("cartesian data mismatch.\nwant:\n%v\ngot:\n%v", mat.Formatted(want), mat.Formatted(got))
|
{1, 0, 3},
|
||||||
}
|
{1, 1, 0},
|
||||||
gotTo := mat.NewDense(8, 3, nil)
|
{1, 1, 1},
|
||||||
Cartesian(gotTo, data)
|
{1, 1, 2},
|
||||||
if !mat.Equal(want, got) {
|
{1, 1, 3},
|
||||||
t.Errorf("cartesian data mismatch with supplied.\nwant:\n%v\ngot:\n%v", mat.Formatted(want), mat.Formatted(gotTo))
|
{1, 2, 0},
|
||||||
}
|
{1, 2, 1},
|
||||||
|
{1, 2, 2},
|
||||||
// Test that Cartesian generates unique vectors.
|
{1, 2, 3},
|
||||||
for cas, data := range [][][]float64{
|
|
||||||
{{1}, {2, 3}, {8, 9, 10}},
|
|
||||||
{{1, 10}, {2, 3}, {8, 9, 10}},
|
|
||||||
{{1, 10, 11}, {2, 3}, {8}},
|
|
||||||
} {
|
|
||||||
cart := Cartesian(nil, data)
|
|
||||||
r, c := cart.Dims()
|
|
||||||
if c != len(data) {
|
|
||||||
t.Errorf("Case %v: wrong number of columns. Want %v, got %v", cas, len(data), c)
|
|
||||||
}
|
|
||||||
wantRows := 1
|
|
||||||
for _, v := range data {
|
|
||||||
wantRows *= len(v)
|
|
||||||
}
|
|
||||||
if r != wantRows {
|
|
||||||
t.Errorf("Case %v: wrong number of rows. Want %v, got %v", cas, wantRows, r)
|
|
||||||
}
|
|
||||||
for i := 0; i < r; i++ {
|
|
||||||
for j := i + 1; j < r; j++ {
|
|
||||||
if floats.Equal(cart.RawRowView(i), cart.RawRowView(j)) {
|
|
||||||
t.Errorf("Cas %v: rows %d and %d are equal", cas, i, j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cartTo := mat.NewDense(r, c, nil)
|
|
||||||
Cartesian(cartTo, data)
|
|
||||||
if !mat.Equal(cart, cartTo) {
|
|
||||||
t.Errorf("cartesian data mismatch with supplied.\nwant:\n%v\ngot:\n%v", mat.Formatted(cart), mat.Formatted(cartTo))
|
|
||||||
}
|
}
|
||||||
|
got := Cartesian(lens)
|
||||||
|
if !intSosMatch(want, got) {
|
||||||
|
t.Errorf("cartesian data mismatch.\nwant:\n%v\ngot:\n%v", want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user