diff --git a/2-B-1/go.mod b/2-B-1/go.mod new file mode 100644 index 0000000..bebac64 --- /dev/null +++ b/2-B-1/go.mod @@ -0,0 +1,4 @@ +module algo + +go 1.26.3 + diff --git a/2-B-1/main.go b/2-B-1/main.go new file mode 100644 index 0000000..f339562 --- /dev/null +++ b/2-B-1/main.go @@ -0,0 +1,41 @@ +package algo + +func DP(t1 []int, t2 []int, e1 []int, e2 []int) int { + Len := len(t1) + if len(t2) != Len || len(e1) != Len || len(e2) != Len { + return -1 + } + dp1 := make([]int, Len) + dp2 := make([]int, Len) + dp1[0], dp2[0] = t1[0], t2[0] + + for i := 1; i < Len; i++ { + dp1[i] = min(dp1[i-1], dp2[i-1]+e2[i-1]) + t1[i] + dp2[i] = min(dp2[i-1], dp1[i-1]+e1[i-1]) + t2[i] + } + + return min(dp1[Len-1], dp2[Len-1]) +} + +func Rec(t1 []int, t2 []int, e1 []int, e2 []int) int { + Len := len(t1) + if len(t2) != Len || len(e1) != Len || len(e2) != Len { + return -1 + } + + return min(rec(t1, t2, e1, e2)) +} + +func rec(t1 []int, t2 []int, e1 []int, e2 []int) (int, int, int, int) { + Len := len(t1) + if Len == 1 { + return t1[0], 0x3f3f3f3f3f3f3f3f, 0x3f3f3f3f3f3f3f3f, t2[0] + } + mid := Len / 2 + a11, a12, a21, a22 := rec(t1[:mid], t2[:mid], e1[:mid], e2[:mid]) + b11, b12, b21, b22 := rec(t1[mid:], t2[mid:], e1[mid:], e2[mid:]) + s1 := e1[mid-1] + s2 := e2[mid-1] + return min(a11+b11, a11+s1+b21, a12+s2+b11, a12+b21), min(a11+b12, a11+s1+b22, a12+s2+b12, a12+b22), + min(a21+b11, a21+s1+b21, a22+b21, a22+s2+b11), min(a21+b12, a21+s1+b22, a22+b22, a22+s2+b12) +} diff --git a/2-B-1/main_test.go b/2-B-1/main_test.go new file mode 100644 index 0000000..0302b25 --- /dev/null +++ b/2-B-1/main_test.go @@ -0,0 +1,75 @@ +package algo + +import ( + "fmt" + "math/rand" + "testing" +) + +type testCase struct { + t1 []int + t2 []int + e1 []int + e2 []int +} + +func randomCase(rng *rand.Rand, n int) testCase { + tc := testCase{ + t1: make([]int, n), + t2: make([]int, n), + e1: make([]int, n), + e2: make([]int, n), + } + + for i := 0; i < n; i++ { + tc.t1[i] = rng.Intn(1000) + tc.t2[i] = rng.Intn(1000) + tc.e1[i] = rng.Intn(1000) + tc.e2[i] = rng.Intn(1000) + } + + return tc +} + +func TestRecAndDPMatchOnRandomData(t *testing.T) { + rng := rand.New(rand.NewSource(1)) + const casesPerLen = 200 + + for n := 1; n <= 128; n++ { + for caseIdx := 0; caseIdx < casesPerLen; caseIdx++ { + tc := randomCase(rng, n) + gotRec := Rec(tc.t1, tc.t2, tc.e1, tc.e2) + gotDP := DP(tc.t1, tc.t2, tc.e1, tc.e2) + if gotRec != gotDP { + t.Fatalf("mismatch for len=%d case=%d: Rec=%d DP=%d t1=%v t2=%v e1=%v e2=%v", + n, caseIdx, gotRec, gotDP, tc.t1, tc.t2, tc.e1, tc.e2) + } + } + } +} + +func BenchmarkDP(b *testing.B) { + benchmarkByLen(b, "DP", DP) +} + +func BenchmarkRec(b *testing.B) { + benchmarkByLen(b, "Rec", Rec) +} + +func benchmarkByLen(b *testing.B, name string, fn func([]int, []int, []int, []int) int) { + lengths := []int{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000} + + for _, n := range lengths { + n := n + b.Run(fmt.Sprintf("%s/len=%d", name, n), func(b *testing.B) { + rng := rand.New(rand.NewSource(int64(n))) + tc := randomCase(rng, n) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = fn(tc.t1, tc.t2, tc.e1, tc.e2) + } + }) + } +} diff --git a/2-B-2/go.mod b/2-B-2/go.mod new file mode 100644 index 0000000..48059f0 --- /dev/null +++ b/2-B-2/go.mod @@ -0,0 +1,3 @@ +module algo + +go 1.26.3 diff --git a/2-B-2/main.go b/2-B-2/main.go new file mode 100644 index 0000000..53612b6 --- /dev/null +++ b/2-B-2/main.go @@ -0,0 +1,52 @@ +package algo + +func DP(arr []int) int { + n := len(arr) - 1 + if n <= 0 { + return 0 + } + + const inf = int(^uint(0) >> 1) + dp := make([][]int, n) + for i := range dp { + dp[i] = make([]int, n) + } + + for length := 2; length <= n; length++ { + for l := 0; l+length-1 < n; l++ { + r := l + length - 1 + dp[l][r] = inf + for k := l; k < r; k++ { + cost := dp[l][k] + dp[k+1][r] + arr[l]*arr[k+1]*arr[r+1] + if cost < dp[l][r] { + dp[l][r] = cost + } + } + } + } + + return dp[0][n-1] +} + +func Greedy(arr []int) int { + if len(arr) <= 2 { + return 0 + } + + dims := append([]int(nil), arr...) + cost := 0 + + for len(dims) > 2 { + best := 1 + for i := 2; i < len(dims)-1; i++ { + if dims[i] > dims[best] { + best = i + } + } + + cost += dims[best-1] * dims[best] * dims[best+1] + dims = append(dims[:best], dims[best+1:]...) + } + + return cost +} diff --git a/2-B-2/main_test.go b/2-B-2/main_test.go new file mode 100644 index 0000000..8f04b1e --- /dev/null +++ b/2-B-2/main_test.go @@ -0,0 +1,61 @@ +package algo + +import ( + "math/rand" + "testing" +) + +func TestDPKnownCase(t *testing.T) { + arr := []int{30, 35, 15, 5, 10, 20, 25} + want := 15125 + + if got := DP(arr); got != want { + t.Fatalf("DP(%v) = %d, want %d", arr, got, want) + } +} + +func TestGreedyRandomCompare(t *testing.T) { + r := rand.New(rand.NewSource(1)) + + for tc := 0; tc < 500; tc++ { + matrixCount := r.Intn(5) + 3 + arr := make([]int, matrixCount+1) + for i := range arr { + arr[i] = r.Intn(9) + 2 + } + + greedy := Greedy(arr) + dp := DP(arr) + if greedy < dp { + t.Fatalf("greedy beat dp unexpectedly: arr=%v greedy=%d dp=%d", arr, greedy, dp) + } + } +} + +func TestGreedyIsNotAlwaysOptimal(t *testing.T) { + r := rand.New(rand.NewSource(2)) + found := false + + for tc := 0; tc < 500; tc++ { + matrixCount := r.Intn(5) + 3 + arr := make([]int, matrixCount+1) + for i := range arr { + arr[i] = r.Intn(9) + 2 + } + + greedy := Greedy(arr) + dp := DP(arr) + if dp < greedy { + t.Logf("found counterexample: arr=%v greedy=%d dp=%d", arr, greedy, dp) + found = true + } + + if dp > greedy { + t.Fatalf("why greedy < dp???") + } + } + + if !found { + t.Fatal("expected to find at least one case where dp < greedy") + } +}