>

자연의 고유 한 자연의 합계를 가져와야합니다

내가 생각 해낸 것은 :

type Sum = Stream[Int] //should maintain the invariant that
                       //* all Ints are positive
                       //* is non-strictly decreasing
                       //* is non-empty
def makesums(i: Int): Stream[Sum] = {
  /* Schmear will try to "schmear" a sum by decreasing the first number by one,
  and adding one again in the first position that won't violate the above invariants.
  This is possible when the stream is not all 1's, and the above invariants are met for
  the argument.
  returns an Option[Sum], which is Some(schmeared) if the above is possible,
  and None otherwise.
  */
  def schmear(sum: Sum): Option[Sum] = sum match {
    //if we can decrease the head and increase the next element while
    //staying ordered, do that
    case head #:: s #:: tail if (head - 1 >= s + 1) =>
      Some((head - 1) #:: (s + 1) #:: tail)
    //otherwise, if the head is only one larger than the second element, do the same,
    //but smear the tail, restoring the invariant
    case head #:: s #:: tail if head > s =>
      schmear((s + 1) #:: tail).map(nt => (head - 1) #:: nt)
    //otherwise, if there are at least two elements, just schmear the tail, 
    //and keep the orignal head
    case head #:: s #:: tail =>
      schmear(s #:: tail).map(nt => head #:: nt)
    //otherwise, if the head is larger than 1, decrease by 1, and put a 1 at the end
    case head #:: tail if (head > 1) =>
      Some((head - 1) #:: tail  #::: Stream(1))
    //otherwise, it's not possible.
    case _ => None
  }
  def rec(sum: Sum): Stream[Sum] = {
    schmear(sum) match {
      case Some(schmeared) => sum #:: rec(schmeared)
      case None            => Stream(sum)
    }
  }
  //initiate the recursive algorithm with the sum of the identity    
  rec(Stream(i))
}

이것을 사용하기 :

와이즈 비즈

알고리즘 살펴보기 :

  • println(makesums(5).map(_.toList).toList)

    List(List(5), List(4, 1), List(3, 2), List(2, 2, 1), List(2, 1, 1, 1), List(1, 1, 1, 1, 1))

     사례 4를 취하여 Stream(5) 를 산출
  • Stream(4, 1)  사례 1을 취하여 Stream(4, 1) 를 생성
  • Stream(3, 2)   Stream(3, 2) 로 시작하는 사례 2를 취합니다. , (3 - 1) = 2 로 되풀이  꼬리를 위해
    • Stream(2 + 1)  사례 4를 취하여 Stream(3) 를 산출
  • 와이즈 위즈
  • Stream(2, 1)   Stream(2, 2, 1) 로 시작하는 사례 3을 취합니다. , Stream(2, 2, 1) 로 되풀이
    • 2   Stream(2, 1) 로 시작하는 사례 2를 취합니다. , Stream(2, 1) 로 되풀이  꼬리를 위해
    • (2 - 1) = 1  사례 4를 취하여 Stream(2) 를 산출
    • 와이즈 위즈
  • 와이즈 위즈
  • Stream(2)  사례 2를 취하고 Stream(1, 1) 를 복용  머리를 위해, 그리고 Stream(1, 1, 1) 로 되풀이  꼬리를 위해
    • Stream(2, 1, 1, 1) 를 가질 때까지 계속 사례 2를 복용하십시오.
    • Stream(2, 1, 1, 1)  사례 4를 취하여 1 를 산출
  • 와이즈 위즈

그러나 정말로 복잡하다고 느낀다면. 특히 Schmearing에 대해 5 가지 경우가 있는데 이는 자신이하는 일을 설명하는 것보다 훨씬 복잡해 보입니다.

이 작업을 단순화하기 위해 어떻게해야합니까?

Stream(2, 1, 1)
  • 답변 # 1

    여러분이 구별해야 할 다섯 가지 경우를 제외하고는 코드 자체의 심각한 문제 : 일부 합계가 누락되었습니다. 당신의 합계에 5를 더한 합계의 예는 누락 된 합계 3 + 1 + 1입니다. 그만큼 숫자가 높을수록 더 많은 금액이 누락됩니다.

    그 이유는 알고리즘이 탐욕스럽게 "schmears" 가능할 때마다 첫 번째 숫자. 그러나 이것은 일어나야 만한다 나머지 금액을 더 이상 분석 할 수없는 경우 그래서 당신의 예를 들어 3 + 2가 있고 분할 대신 3을 줄이려고합니다. 2를 먼저 올리세요.

    그것을 고치면 중요한 결과는 충분하지 않다는 것입니다 혼자서 schsuming하여 합계를 수정하지만 부품을 "재설정"해야합니다 그것의 가끔. 3 + 2에서 3 + 1 + 1로 가면 도달 할 수 없습니다 더 이상 schmearing으로 2 + 2 + 1

    더 나은 전략은 먼저 꼬리를 깎아내는 것입니다. 더 이상 가능하지 않습니다. 첫 번째 숫자를 줄이고 꼬리를 불변량을 위반하지 않는 가장 간단한 형태. 이것은 무엇입니까 와이즈 비즈  다음 목록에 있습니다. 총합이 10이라고합시다 첫 번째 숫자를 3으로 줄이면 합계가 3 + 3 + 3 + 1로 설정됩니다.

    uniformSum
    
    

    또한 적은 수의 사례를 구별하는 것으로 충분하다는 것을 알 수 있습니다. 2의 예견이 필요하지 않습니다. 흔들리지 않는 합계 (빈 또는 1로 시작), 우리는 우리가 할 수있는 경우에만 관심 꼬리를 바르거나 말거나.

    매우 컴팩트 한 형태로 작성하려고했습니다. 기능을 생각하면 더 나은 가독성을 위해 더 자세하게 설명하십시오. 나도 알아.

  • 답변 # 2

    확인 문제를 이해하고 있다고 생각합니다 : 정수 type Sum = List[Int] def sums(n: Int): Stream[Sum] = schmearStream(List(n)) def schmearStream(sum: Sum): Stream[Sum] = sum #:: (schmear(sum) map (schmearStream(_)) getOrElse Empty) def schmear(nums: Sum): Option[Sum] = if (nums.isEmpty || nums.head == 1) None else schmear(nums.tail) match { case Some(schmearedTail) => Some(nums.head :: schmearedTail) case None => Some(uniformSum(nums.sum, nums.head - 1)) } def uniformSum(sum: Int, num: Int): Sum = { val rest = sum % num List.fill(sum / num)(num) ::: (if (rest == 0) Nil else List(rest)) } , n 의 모든 정수 파티션의 목록 (또는 스트림)을 생성하려고합니다 . 재주문까지의 독창성을 촉진하기 위해 모든 목록의 순서가 약하게 줄어드는 요구 사항을 적용합니다.

    n 유형을 실제로 만들어야하는지 잘 모르겠습니다. . 나머지 코드에서 얼마나 자주 해당 유형을 사용하는지에 따라 다릅니다. 어느 쪽이든 이름을 변경해야합니다. .

    귀하의 코드를 읽을 수 있다고 생각하며, 알고리즘으로는 5 가지 경우가 있다고 생각하지 않습니다. 어쨌든 다섯 건은 그리 많지 않습니다. 그러나 사례를 좀 더 명확하게해야합니다.

    첫 번째 사례를 작성하는보다 명확한 방법은

    type Sum = Stream[Int]

    그리고 두번째 사건은

    Parition

    보다 명확합니다.

    대체로 여기 내 구현이 있습니다. 내 코드가 좀 더 선형 적이 될 수 있도록 (당신이 아닌 경우) 다른 경로를 갔다. 나쁘거나 나쁘지 않습니다. 내 코드는 다음과 같습니다.

    case head #:: s #:: tail if (head - 2 >= s)
    
    

    내부 목록이 크지 않아야하므로 목록 스트림으로 선택했습니다.

  • 답변 # 3

    유형

    case head #:: s #:: tail if head - 1 == s 를 나타내는  Wyzwyz로  이해가되지 않습니다. 각 합계는 관리 가능한 크기이므로 object DistinctSums { private def allDistinctSumsHelper(n: Int, upperBound: Int): Stream[List[Int]] = { n match { case 0 => Stream(Nil) case 1 => Stream(List(1)) case _ => (1 to upperBound) .toStream .map(x => allDistinctSumsHelper(n-x, x) .map(xs => x::xs) ).flatten .toStream } } def allDistinctSums(n: Int) = allDistinctSumsHelper(n, n) def main(args: Array[String]): Unit = { println(allDistinctSums(6, 6) take 8 toList) } }  잘 될 것입니다.

    보기, 스트림 및 반복자의 차이점은 무엇입니까?, 나는 Sum 라고 말할 것입니다.   Stream[Int] 보다 더 적합하다 . 결과는 무한하지 않으므로 캐시 할 필요가 없습니다. 당신이 List[Int] 를 원한다면 , 당신은 쉽게 Iterator[Sum] 를 호출 할 수 있습니다  결과에.

    버그

    샘플 출력에서 ​​알 수 있듯이 솔루션이 5 = 3 + 1 + 1을 분해하지 못합니다.

    알고리즘 Stream[Sum] 로 출력하면 솔루션이 더 간단합니다.  마지막으로 Stream 를 감지하는 것보다 해당 사례를 감지하는 것이 더 쉽기 때문에 .

    toStream 때문에 헤드 엔드에서 목록을 조작하는 것이 좋습니다.  연결은 효율성에 좋지 않습니다. 마지막 예는 개선점을 보여줍니다.

    이 자바 코드를 기반으로 한 솔루션은 다음과 같습니다 : 알고리즘에 대한 @rolfl의 크레딧 :

    List(n)
    
    

    <시간>

    실행 예 :

    List(1, 1, 1, …, 1)
    
    

    이 출력 순서가 원래 순서보다 더 효율적임을 알 수 있습니다. 예를 들어, #::: 에서   /** List of non-increasing positive integers */ type Sum = List[Int] def sums(n: Int): Iterator[Sum] = { /** Prepends the head of the stack repeatedly until a total amount has * been added. The leading element of the result is then increased * so that the total added is exactly the specified amount. */ @tailrec def topUp(amount: Int, stack: List[Int]): List[Int] = { if (amount < stack.head) (stack.head + amount) :: stack.tail else topUp(amount - stack.head, stack.head :: stack) } /** Returns the next partition by popping the first element, increasing the * next element, and topping it up to the same total. Returns None if * the input has only one element. */ def advance(partition: Option[Sum]): Option[Sum] = { partition.get match { case List(n) => None case p => Some(topUp(p.head - 1, p.tail.head + 1 :: p.tail.tail)) } } Iterator.iterate(Some(List.fill(n)(1)): Option[Sum])(advance) .takeWhile(! _.isEmpty) .map(_.get) } 로   scala> sums(5).foreach(println) List(1, 1, 1, 1, 1) List(2, 1, 1, 1) List(3, 1, 1) List(2, 2, 1) List(4, 1) List(3, 2) List(5) 에서가는 동안 팝 팝 푸시 만 포함됩니다.   List(1, 1, 1, 1, 1) 로  모든 것을 다시 작성해야합니다.

    List(2, 1, 1, 1)

  • 이전 스칼라와 일부 Scalaz를 사용한 간단한 아나그램 파인더
  • 다음 c# - 블로흐의 빌더 패턴/업데이터