본문 바로가기

Computer Science/알고리즘

[Swift]세탁 횟수 같게 만들기 1 - 안 쓰는 속옷 없게 만들기

이전 포스팅(https://taeminator1.tistory.com/70)에서 살펴봤듯이, 일반적으로 속옷을 세탁할 때, 시간이 지나면 각각의 속옷의 세탁 횟수가 달라진다. 이번 시간에는 이러한 문제를 해결할 수 있는 방법을 살펴보려고 한다.

세탁 횟수에 영향을 미치는 요인을 두 가지로 정리한 적이 있다.

  1. 일정하지 않은 세탁물의 양
  2. 입을 속옷을 선택하는 방법

따라서 위에 두 요인에 대한 해결책을 제시한 후에, 시뮬레이션으로 구현하여 결과를 확인해 보려고 한다. 그전에 속옷을 입고 세탁하는 등의 과정을 아래와 같이 정리해보았다. 사실 이전 포스팅에서 다 말한 내용이다.

세탁 라이프는 다음과 같이 4단계의 반복으로 표현 가능하다.

  1. 사용자 그날 속옷을 입기로 하였다면 서랍에서 속옷을 꺼내 입는다.
  2. 속옷을 사용하고 난 뒤 세탁 바구니에 넣는다.
  3. 세탁하는 날이 되면 세탁 바구니에 있는 속옷을 세탁한다.
  4. 건조가 다 된 옷을 서랍에 넣는다.


세탁 횟수에 영향을 미치는 요인 중 2번을 살펴보자. 이전 포스팅(https://taeminator1.tistory.com/70)에서 살펴보았지만, 2번에 의해서 각각의 속옷의 세탁 횟수가 달라졌다. 심지어 경우에 따라 사용하지 않는 속옷도 생겼다. 나는 사용하지 않는 속옷에 집중하여, "만약 모든 속옷을 사용하게 한다면, 세탁 횟수가 조금 더 일정해지지 않을까"라고 생각하였다. 그 결과 서랍에 속옷이 없어질 때까지, 세탁된 속옷이 추가되지 않는다면 문제는 간단히 해결될 것 같았다.

이를 시뮬레이션으로 구현해 보자. 이전 normalLaundrySimulator를 복사/붙여 넣은 다음, advancedLaundrySimulator1로 이름을 변경한다. 그다음 아래와 같이 임시 바구니 tmpWashedBasket을 추가하였다. 위 그림에서 건조대의 역할을 한다고 생각하면 이해가 쉬울 것이다. 그리고 세탁한 후 서랍으로 바로 가는 것이 아니라, 임시 바구니로 보내면 된다(건조대에 그대로 남겨두면 된다).

func advancedLaundrySimulator1(stuffNumber: UInt, washTerm: UInt, repeatNumber: UInt, possibility: Double = 1.0, priority: SelectionPriority) -> [Stuff] {
    
    ...
    
    var washedBasket: Basket = []                           // 세탁된 옷을 넣는 바구니1
    var laundryBasket: Basket = []                          // 세탁할 옷을 넣는 바구니2
    var tmpWashedBasket: Basket = []                        // 세탁된 옷을 보관하는 임시 바구니
    
    ...
    
    while washedNumber < repeatNumber {                     // repeatNumber 만큼 세탁 가능
        var day: Int = 0
        while day < washTerm {                              // 세탁을 위한 주기 카운트
            let isPossible: Bool = { Double.random(in: 0.0 ..< 1.0) < possibility }()
            if isPossible {                                 // 속옷 입는 날
                
                ...
                
            }
            day += 1
        }
        laundryBasket.doTheWash()                           // 세탁하기
        laundryBasket.move(to: &tmpWashedBasket)            // 임시 바구니에 넣기
        washedNumber += 1
    }
    
    ...
    
}


그럼 임시 바구니에 있는 속옷을 언제 서랍으로 옮길까? 속옷을 서랍에서 꺼내고 난 뒤, 서랍이 비어있으면 옮기면 된다. 이를 코드로 구현하면 아래와 같다. 속옷을 사용하고 세탁 바구니에 넣는 것이 한 줄의 코드로 이루어져 있어, 그다음에 서랍을 확인하였다.

if isPossible {                                 // 속옷 입는 날
    
    ...
    
    if washedBasket.isEmpty {                   // 바구니1이 비어있을 경우, 임시 바구니에서 가져오기
        tmpWashedBasket.move(to: &washedBasket)
    }
}


이렇게 구현을 하고 이전과 같이 입을 속옷을 선택하는 방법에 따라 총 세 번의 시뮬레이션을 돌려 보았다.


예상한 대로 priority가 변해도, 전혀 사용하지 않는 속옷은 하나도 없게 되었다. (첫 번째, 두 번째 실험 결과)

그리고 추가로, priority를 .randomly로 설정했을 때, 표준편차가 눈에 띄게 줄어들었다. (아래 결과와 비교)

normalLaundrySimulator 결과

이전에는 최솟값, 최댓값이 각각 46, 63이었는데, 새로 만든 시뮬레이터에서는 53, 58이다. 반복된 실험에서 비슷한 경향으로 나왔다. 이는 무작위로 선택하는 범위를 임시 바구니가 어느 정도 조절해서 생긴 결과이다.

하지만 여전히, 세탁 횟수가 일정하지는 않다. 특히 priority를 .byPreference로 설정하는 경우, 그 차이가 가장 심하다. 다음 포스팅에서는 이런 세탁 횟수의 차이마저 없애는 방법을 살펴보자.

*추가 사항
중요한 작업이 아직 남아 있다. 바로 guard문이다. 딱히 추가될 것이 없어 보이지만, 속옷의 개수와 세탁 주기가 같은 경우, 만약 모든 속옷이 임시 바구니에 있다면 속옷을 선택할 수 없는 경우가 생긴다. 따라서 속옷의 개수는 세탁 주기보다 무조건 1 이상 커야 한다.

guard stuffNumber >= washTerm + 1 else {
    fatalError("The number of stuff must be more than the washing term")
}