ソースを参照

K5 Some egyptian fractions.

Frederic G. MARAND 4 年 前
コミット
90b423947b

+ 11 - 0
go/.idea/runConfigurations/k5_some_egyptian_fractions.xml

@@ -0,0 +1,11 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="k5 some egyptian fractions" type="GoTestRunConfiguration" factoryName="Go Test">
+    <module name="codewars" />
+    <working_directory value="$PROJECT_DIR$/" />
+    <go_parameters value="-i" />
+    <framework value="gotest" />
+    <kind value="PACKAGE" />
+    <package value="code.osinet.fr/fgm/codewars/kyu5/some_egyptian_fractions" />
+    <method v="2" />
+  </configuration>
+</component>

+ 92 - 0
go/kyu5/some_egyptian_fractions/k.go

@@ -0,0 +1,92 @@
+package kata
+
+import (
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+)
+
+func normalizeRational(s string) (n, d int) {
+	sl := strings.Split(s, ".")
+	n, _ = strconv.Atoi(sl[0])
+	if len(sl) == 1 {
+		return n, 1
+	}
+
+	var f float64
+	fmt.Sscanf(s, "%f", &f)
+	fd := math.Pow10(len(sl[1]))
+	n = int(f * fd)
+	d = int(fd)
+	g := gcd(n, d)
+	return n / g, d / g
+}
+
+func toFraction(s string) (n, d int) {
+	sl := strings.Split(s, "/")
+	n, d = normalizeRational(sl[0])
+	if len(sl) == 1 {
+		return
+	}
+	n2, d2 := normalizeRational(sl[1])
+	n, d = n * d2, d * n2
+	g := gcd(n, d)
+	return n / g, d / g
+}
+
+// gcd from runtime/proc.go
+// (c) Go Authors - MIT License
+func gcd(a, b int) int {
+	for b != 0 {
+		a, b = b, a%b
+	}
+	return a
+}
+
+func fib(divs chan<- int, a, b int) {
+	if a == 1 || a == 0 {
+		divs <- b
+		close(divs)
+		return
+	}
+
+	// Smallest n larger than b/a or equal to it.
+	r := int(math.Ceil(float64(b) / float64(a)))
+	divs <- r
+
+	n := a*r - b
+	d := r * b
+	g := gcd(n, d)
+	n /= g
+	d /= g
+	fib(divs, n, d)
+}
+
+func Decompose(s string) []string {
+	n, d := toFraction(s)
+	res := []string{}
+	// Handle degenerate cases first.
+	// Just 0.
+	if n == 0 {
+		return res
+	}
+	// Larger than 1: start by floor(ratio).
+	if n/d >= 1 {
+		first := n / d
+		res = append(res, fmt.Sprintf("%d", first))
+		if n%d == 0 {
+			return res
+		}
+		n -= first * d
+	}
+
+	// Now code can assume n != 0.
+	divs := make(chan int)
+	fmt.Println(s, n, d)
+	go fib(divs, n, d)
+	for div := range divs {
+		res = append(res, fmt.Sprintf("1/%d", div))
+	}
+	return res
+}

+ 35 - 0
go/kyu5/some_egyptian_fractions/k_test.go

@@ -0,0 +1,35 @@
+package kata_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "code.osinet.fr/fgm/codewars/kyu5/some_egyptian_fractions"
+)
+
+func doTest(s string, exp []string) {
+	var ans = Decompose(s)
+	Expect(ans).To(Equal(exp))
+}
+
+var _ = Describe("Tests Decompose", func() {
+
+	It("should handle 21/23", func() {
+		doTest("21/23", []string{"1/2", "1/3", "1/13", "1/359", "1/644046"})
+	})
+	It("should handle 12/4", func() {
+		doTest("12/4", []string{"3"})
+	})
+	It("should handle 0.66", func() {
+		doTest("0.66", []string{"1/2", "1/7", "1/59", "1/5163", "1/53307975"})
+	})
+	It("should handle 0", func() {
+		doTest("0", []string{})
+	})
+	It("should handle 1", func() {
+		doTest("1", []string{"1"})
+	})
+	It("should handle 1.25", func() {
+		doTest("1.25", []string{"1", "1/4"})
+	})
+})

+ 13 - 0
go/kyu5/some_egyptian_fractions/some_egyptian_fractions_suite_test.go

@@ -0,0 +1,13 @@
+package kata_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestSomeEgyptianFractions(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "SomeEgyptianFractions Suite")
+}

+ 64 - 0
go/kyu5/some_egyptian_fractions/topic.md

@@ -0,0 +1,64 @@
+# Some egyptian fractions
+
+Given a rational number n
+
+n >= 0, with denominator strictly positive
+
+- as a string (example: "2/3" in Ruby, Python, Clojure, JS, CS, Go) or as two 
+  strings (example: "2" "3" in Haskell, Java, CSharp, C++, Swift)
+- or as a rational or decimal number (example: 3/4, 0.67 in R)
+  
+decompose this number as a sum of rationals with numerators equal to one and
+without repetitions (2/3 = 1/2 + 1/6 is correct but not 2/3 = 1/3 + 1/3, 1/3 is
+repeated).
+
+The algorithm must be "greedy", so at each stage the new rational obtained in
+the decomposition must have a denominator as small as possible. In this manner
+the sum of a few fractions in the decomposition gives a rather good approximation
+of the rational to decompose.
+
+2/3 = 1/3 + 1/3 doesn't fit because of the repetition but also because the first
+1/3 has a denominator bigger than the one in 1/2 in the decomposition
+2/3 = 1/2 + 1/6.
+
+## Example:
+
+(You can see other examples in "Sample Tests:")
+
+`decompose("21/23")` or `"21" "23"` or `21/23` should return 
+    
+    ["1/2", "1/3", "1/13", "1/359", "1/644046"] in Ruby, Python, Clojure, JS, CS, Haskell, Go
+    
+    "[1/2, 1/3, 1/13, 1/359, 1/644046]" in Java, CSharp, C++
+    
+    "1/2,1/3,1/13,1/359,1/644046" in C, Swift, R
+
+## Notes
+
+1) The decomposition of 21/23 as
+
+    21/23 = 1/2 + 1/3 + 1/13 + 1/598 + 1/897
+
+is exact but don't fulfill our requirement because 598 is bigger than 359. Same for
+
+    21/23 = 1/2 + 1/3 + 1/23 + 1/46 + 1/69 (23 is bigger than 13)
+    or 
+    21/23 = 1/2 + 1/3 + 1/15 + 1/110 + 1/253 (15 is bigger than 13).
+
+2) The rational given to decompose could be greater than one or equal to one, in
+    which case the first "fraction" will be an integer (with an implicit
+    denominator of 1).
+
+3) If the numerator parses to zero the function "decompose" returns [] (or "".
+
+4) The number could also be a decimal which can be expressed as a rational.
+
+### examples:
+
+`0.6` in Ruby, Python, Clojure,JS, CS, Julia, Go
+
+`"66" "100"` in Haskell, Java, CSharp, C++, C, Swift, Scala, Kotlin
+
+`0.67` in R.
+
+**Ref**: http://en.wikipedia.org/wiki/Egyptian_fraction