항목을 "균등하게"배포하는 알고리즘


25

결과 목록이 가능한 한 "균형"또는 "균등하게 분산"되도록 목록에서 값을 배포하는 알고리즘을 찾고 있습니다 (따옴표로 표시하는 것이 최선의 방법인지 확실하지 않기 때문에 따옴표로 표시) ... 나중에 결과가 다른 것보다 나은지 측정하는 방법을 제공합니다).

따라서 목록은 다음과 같습니다.

[1, 1, 2, 2, 3, 3]

값을 재분배 한 후 가장 좋은 결과 중 하나는 다음과 같습니다.

[1, 2, 3, 1, 2, 3]

이 결과만큼 좋은 결과가있을 수 있으며 물론 덜 균일 한 값 집합으로 인해 더 복잡해집니다.

결과가 다른 것보다 나은지 측정하는 방법은 다음과 같습니다.

  1. 각 항목과 다음 항목 사이의 거리를 같은 값으로 계산하십시오.

  2. 해당 거리 세트의 표준 편차를 계산하십시오. 분산이 적을수록 더 좋은 결과를 얻을 수 있습니다.

관찰 :

  • 거리를 계산하고 동일한 값을 가진 항목을 찾지 않고 목록의 끝에 도달하면 목록의 처음으로 돌아갑니다. 따라서 대부분 동일한 항목이 발견되며 해당 항목의 거리는 목록의 길이가됩니다. 이것은리스트가 주기적 이라는 것을 의미한다 .
  • 일반적인 목록에는 다양한 수량으로 ~ 15 개의 다른 값을 가진 ~ 50 개의 항목이 있습니다.

그래서:

  • 결과적 [1, 2, 3, 1, 2, 3]으로 거리는 [3, 3, 3, 3, 3, 3]이고 표준 편차는입니다 0.
  • 결과적 [1, 1, 2, 2, 3, 3]으로 거리는 [1, 5, 1, 5, 1, 5]이고 표준 편차는입니다 2.
  • 첫 번째 결과가 두 번째 결과보다 낫습니다 (편차가 낮을수록 좋습니다).

이러한 정의가 주어지면 검색해야 할 알고리즘이나 전략에 대한 단서가 필요합니다.


적어도 대략적으로 파티션 문제 의 (최적화 변형) 문제 를 해결하려는 것처럼 보입니다 . 아마도 그 알고리즘에는 많은 알고리즘이있을 것입니다!
Raphael

이것을 다시 읽으면 왜 모든 값의 발생을 계산 한 다음 주기적으로 값을 배치하는 것이 항상 최적의 솔루션을 산출하지는 않습니까?
Raphael

답변:


8

비슷한 문제를 연구하는 동안이 질문에 부딪 혔습니다 : 층화를 줄이기 위해 액체를 최적으로 첨가하십시오. 내 솔루션이 귀하의 상황에도 적용되는 것 같습니다.

액체 A, B 및 C를 30,20,10 비율 (즉, 30 단위 A, 20 단위 B 및 10 단위 C)로 혼합하려는 경우 모두 추가하면 계층화로 끝납니다 더 작은 단위를 섞는 것이 좋습니다. 예를 들어, 시퀀스 [A, B, A, C, B, A]에서 단일 단위 추가를 수행하십시오. 그것은 계층화를 완전히 막을 것입니다.

내가 찾은 방법은 우선 순위 대기열을 사용하여 일종의 병합으로 처리하는 것입니다. 추가 사항을 설명하는 구조를 만드는 경우 :

MergeItem
    Item, Count, Frequency, Priority

주파수는 "N마다 1"로 표시됩니다. 따라서 6 번 중 3 번을 더한 A의 빈도는 2 (6/3)입니다.

그리고 초기에 다음을 포함하는 힙을 초기화하십시오.

(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)

이제 힙에서 첫 번째 항목을 제거하여 출력합니다. 그런 다음 계수를 1 씩 줄이고 빈도 별 우선 순위를 높이고 다시 힙에 추가하십시오. 결과 힙은 다음과 같습니다.

(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)

그런 다음 힙에서 B를 제거하고 출력하여 업데이트 한 다음 힙에 다시 추가하십시오.

(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)

그런 식으로 계속하면 원하는 혼합물을 얻습니다. 동일한 우선 순위 항목이 힙에 삽입 될 때 빈도 값이 가장 높은 항목 (즉, 빈도가 가장 적은 항목)이 먼저 주문되도록 사용자 정의 비교자를 사용합니다.

내 블로그에 문제와 솔루션에 대한 자세한 설명을 작성하고 작동하는 C # 코드를 제시했습니다. 목록에 항목 고르게 분배하기를 참조하십시오 .

댓글 후 업데이트

내 문제는 OP의 문제와 유사하다고 생각하므로 내 솔루션이 잠재적으로 유용합니다. OP의 질문 측면에서 답변을 더 틀리지 않은 점에 대해 사과드립니다.

내 솔루션이 0, 1 및 2 대신 A, B 및 C를 사용한다는 첫 번째 이의 제기는 쉽게 해결됩니다. 그것은 단순히 명명법의 문제입니다. 나는 "두 개의 1"보다는 "두 개의 A"를 생각하고 말하는 것이 더 쉽고 혼란스럽지 않다는 것을 안다. 그러나이 토론의 목적을 위해 OP의 명명법을 사용하도록 아래 출력을 수정했습니다.

물론 제 문제는 거리의 개념을 다룹니다. "사물을 골고루 퍼 뜨리려면"거리가 암시됩니다. 그러나 다시 한 번, 내 문제가 OP의 문제와 어떻게 비슷한 지 제대로 보여주지 못한 것은 저의 실패였습니다.

OP가 제공 한 두 가지 예제로 몇 가지 테스트를 실행했습니다. 그건:

[1,1,2,2,3,3]  // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]

나의 명명법에서 그것들은 각각 [2,2,2]와 [4,3,2,1]로 표현됩니다. 즉, 마지막 예에서 "0 유형의 4 항목, 1 유형의 3 항목, 2 유형의 2 항목 및 3 유형의 1 항목"입니다.

테스트 프로그램을 실행하고 (아래에 설명 된대로) 결과를 게시했습니다. OP의 입력이 없으면 결과가 그의 결과와 비슷하거나 더 나쁘거나 더 나은지 말할 수 없습니다. 다른 사람이 게시하지 않았기 때문에 내 결과를 다른 사람의 결과와 비교할 수도 없습니다.

나는 알고리즘에 좋은 솔루션을 제공하는, 그러나 말할 수있는 액체를 혼합 할 때 계층화를 제거 문제. 그리고 그것은 OP의 문제에 대한 합리적인 해결책을 제공하는 것처럼 보입니다 .

아래에 표시된 결과를 위해 블로그 항목에 자세히 설명 된 알고리즘을 사용했습니다. 초기 우선 순위는로 설정 Frequency/2하고 힙 비교기는보다 빈번한 항목을 선호하도록 수정했습니다. 수정 된 코드는 여기에 표시되며 수정 된 줄은 주석 처리됩니다.

private class HeapItem : IComparable<HeapItem>
{
    public int ItemIndex { get; private set; }
    public int Count { get; set; }
    public double Frequency { get; private set; }
    public double Priority { get; set; }

    public HeapItem(int itemIndex, int count, int totalItems)
    {
        ItemIndex = itemIndex;
        Count = count;
        Frequency = (double)totalItems / Count;
        // ** Modified the initial priority setting.
        Priority = Frequency/2;
    }

    public int CompareTo(HeapItem other)
    {
        if (other == null) return 1;
        var rslt = Priority.CompareTo(other.Priority);
        if (rslt == 0)
        {
            // ** Modified to favor the more frequent item.
            rslt = Frequency.CompareTo(other.Frequency);
        }
        return rslt;
    }
}

OP의 첫 번째 예제로 테스트 프로그램을 실행하면 다음과 같은 결과를 얻습니다.

Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0

그래서 내 알고리즘은 모든 카운트가 같은 사소한 문제에 작동합니다.

OP가 게시 한 두 번째 문제는 다음과 같습니다.

Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0

나는 그것을 개선 할 분명한 방법을 보지 못합니다. 항목 0 [2,3,2,3] 또는 2와 3의 다른 배열에 대한 거리를 재조정 할 수는 있지만 항목 1 및 / 또는 2의 편차가 변경됩니다. "최적"이이 상황에 있습니다. 더 자주 또는 덜 빈번한 항목에서 더 큰 편차를 갖는 것이 더 낫습니까?

OP의 다른 문제가 없었기 때문에 그의 설명을 사용하여 내 자신의 몇 가지를 구성했습니다. 그는 그의 직책에서 말했다 :

일반적인 목록에는 다양한 수량으로 ~ 15 개의 다른 값을 가진 ~ 50 개의 항목이 있습니다.

그래서 내 두 가지 테스트는 다음과 같습니다.

[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1]  // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1]     // 48 items, 13 types

그리고 내 결과 :

Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0

두 번째 예의 경우 :

Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0

@DW 내 업데이트를 참조하십시오. 내 문제가 OP의 문제와 어떻게 비슷한 지, 알고리즘이 OP의 문제에 대한 해결책을 어떻게 제공하는지 보여줍니다.
Jim Mischel

좋은 물건! 훌륭한 업데이트에 감사드립니다. 공감.
DW

내가 이전에 말했듯이 꽤 흥미 롭습니다. 아이디어의 단순성은 매력적입니다. 나는 그것을주의 깊게 읽을 시간이 없었습니다. 귀하의 솔루션은 실제로 원래 질문의 순환 성을 고려합니까? 목적에 맞게 조정할 수있는 방법이있을 수 있지만 완전히 확신 할 수는 없습니다.
babou

@babou : 결과에서 볼 수 있듯이 거리 계산이 래핑되지만 알고리즘 자체는 OP 문제의 주기적 특성에 대해 특정 허용치를 만들지 않습니다. 알고리즘을 적용 할 수있는 방법도 보지 못합니다. 또는 그 문제에 대해 주기적 특성을 고려하면 결과가 어떻게 향상됩니까? 모든 수를 두 배로 늘리는 것 (즉, [3,2,1]을 [6,4,2]로 변경)을 고려하는 것은 흥미롭지 만, 사실상 같은 것입니다. 내 생각에 알고리즘은 동일한 결과를 생성 할 것입니다.
Jim Mischel

6

이것은 NP-hard 일 수있는 것처럼 "냄새가납니다". 그렇다면 NP-hard 문제가 발생하면 어떻게해야합니까? 휴리스틱이나 근사 알고리즘을 던지거나 SAT 솔버를 사용하십시오.

귀하의 경우, 절대 최적의 솔루션이 필요하지 않은 경우 합리적인 출발점 중 하나는 시뮬레이션 어닐링 을 시도 하는 것 입니다. 후보 솔루션을 가져와 주변의 후보 솔루션으로 옮기는 자연스러운 방법이 있습니다. 목록에서 두 항목을 무작위로 선택하고 교체하십시오. 시뮬레이션 어닐링은 반복적으로 솔루션을 개선하려고 시도합니다. 익숙하지 않은 경우 시뮬레이션 어닐링에서 많은 리소스를 찾을 수 있습니다. 점진적으로 개선 (예 : 표준 거리 편차)하기 위해 후보 솔루션을 약간 변경하는 다른 "로컬 이동"세트를 실험 할 수도 있습니다.

그것이 잘 작동하지 않으면, 두 번째 제안은 이것을 SAT 솔버 에서 던지도록 시도하는 것 입니다. 문제 크기가 작아서 작동 할 수 있습니다. 최적화 함수와 관련된 결정 문제로 시작하십시오. 가 주어지면 표준 편차가 (즉, 분산이 인 솔루션 )가 있습니까? 이것은 SAT 인스턴스로 표현 될 수 있습니다. SAT로 표현하는 것은 지저분하지만 STP와 같은 프론트 엔드를 사용하면 STP가 정수 산술을 지원하므로 조금 더 쉬워집니다. 따라서 부울 알 수없는 가있을 수 있습니다 . 여기서 배열 의 번째 요소에 값 가 있으면 는 true 입니다.t t 2 x i , j x i , j i j t 2ttt2xi,jxi,jij. 이제 이것이 원래 입력의 유효한 순열이라는 제약 조건을 표현할 수 있습니다. 더 많은 정수 미지수를 생성하고 제약 조건을 추가하여 요소 사이의 거리와 동일하게 만든 다음 해당 거리의 분산을 계산하고 이어야한다는 제약 조건을 추가 할 수 있습니다 . 물론 SAT 솔버의 최악의 실행 시간은 기하 급수적이므로 SAT 솔버가이 문제를 해결할 수도 있습니다. 그러나이 문제를 처리 할 수도 있습니다. 시도 할 수있는 또 다른 기술입니다.t2

그러나 시뮬레이션 어닐링으로 시작하는 것이 좋습니다. 그것이 가장 효과가있을 것이라고 생각하기 때문에 이것이 가장 먼저 시도 할 것입니다.


이러한 종류의 일정 문제를 해결하는 표준 방법은 제안입니까? 나는 이것에 대한 상용 소프트웨어가 있다고 생각합니다. 그들은 그것을 어떻게 처리합니까?
babou

@babou, 좋은 질문-나는 모른다.
DW

알고리즘의 세부 사항을 추가로 개발했지만 기존 응용 프로그램에서이를 많이 사용하지 않을 것입니다. 실제로, 스케줄링 애플리케이션이 이런 종류의 문제를 처리하는지 궁금합니다. 방금 한 것처럼 주석 이외의 질문을하는 방법을 보지 못했기 때문에 SE.softwarerecs에 대한 정보를 요청했습니다.
babou

최적의 솔루션은 NP-어려울 수 있습니다. 그러나 상당히 실행 가능한 솔루션은 O (n log k)입니다. 여기서 n은 총 항목 수이고 k는 항목 유형 수입니다. 내 답변과 링크 된 블로그 게시물을 참조하십시오.
Jim Mischel

2

휴리스틱 알고리즘 스케치

이 문제에 대한 정확한 해결책이 없습니다. 그러나 Raphael의 의견에 따르면 휴리스틱 알고리즘이 개발 된 파티션 문제처럼 보이므로 휴리스틱 방식을 시도해 보겠습니다. 이것은 휴리스틱 알고리즘의 스케치 일뿐입니다.

vn[1..n]ini

nvnvn/nv

v

in/ni n / n inmodnin/ni

그것은 우리의 알고리즘을 안내 할 것입니다.

그러나 먼저 싱글 톤 값 (한 번만 발생)은 항상 동일한 관련 거리 갖습니다 . 따라서 이들의 배치는 중요하지 않으며 알고리즘에 의해 무시 될 수 있습니다. 그들은 마지막에 남은 슬롯을 가져갈 것입니다.n

대부분의 이탈 그 거리가 될 필요가 있기 때문에 그런 다음, 가장 정확한 제곱의 합에 덜 기여하고, 우리는이 반드시 일탈 대부분의 장소 즉, 값을 먼저 값을 시도 그런 그 최고입니다.| n / n i - v |i|n/niv|

처음에는 매우 적은 수의 발생이있는 값일 수 있습니다. 점유 슬롯에 의해 생성 된 제약 조건은 배치 된 값의 수 (?)에 비례하기 때문에 실제로 차이가 없다고 생각합니다.

고려되는 첫 번째 값은 제한없이 배치 될 수 있습니다. 그런 다음 표준 편차에 대한 기여를 최소화하기 위해 다른 값을 배치해야하지만 이전에 배치 된 값에 의해 여유 공간이 남아있는 슬롯에만 적용됩니다.

나머지 슬롯에서 값의 발생 위치는 동적 프로그래밍 알고리즘을 사용하여 두 위치 사이에 동일한 수의 값을 배치하는 계산을 병합하고 표준 편차에 최소한의 영향을 미치는 값만 유지하는 계산을 병합 할 수 있습니다 (예 : 편차의 제곱의 합계에 대한 최소값).

때때로, 몇 가지 최소한의 솔루션이있을 것입니다. 이 경우 나머지 슬롯이 가장 고르게 분포 된 최소 솔루션을 선택하여 여유를 유지하려고합니다. TH는 나머지 자유 슬롯 사이의 거리의 표준 편차를 계산하여 각 솔루션에 대해 계산할 수 있습니다 ( 대한 것이 아니라 평균값에 해당함 ).v

그런 다음 나머지 값에 대한 반복 등이싱글 톤이 아닌 모든 값이 배치 될 때까지 계속됩니다.| n / n j - v |j|n/njv|

그런 다음 나머지 슬롯에 싱글 톤 값을 넣습니다.

나는 이것이 일반적으로 합리적인 해결책을 제시해야한다고 생각하지만, 그것을 증명하거나 최적의 해결책과의 격차를 추정하는 방법에 대해서는 아직 모른다.


싱글 톤을 제쳐두고 가장 일반적인 것부터 시작하더라도 중요하지 않다는 인상을 받았습니다. 최상의 결과 를 낳은 전략은 발생별로 값을 정렬하고 가장 많이 발생하는 값부터 순서대로 배치합니다. 이것은 자연스럽게 싱글 톤을 남깁니다.
moraes

@moraes 중요한 것은 값 에서 거리의 평균 편차를 줄임으로써 순서를 정하는 것 입니다. 이것은 일반적으로 가장 적고 가장 일반적인 것을 번갈아 가며 양쪽 끝에서 중간을 향해 시작합니다 ( 는 평균 거리 이므로 가까운 발생 횟수 ). 물론 싱글 톤은 제외했다. n / v Vvn/vV
babou

10 개의 값 [0, 0, 0, 0, 1, 1, 1, 2, 2, 3]과 v가 있는 목록의 경우 4첫 번째 값 1( 10/3 = 3.33, v에 가장 가까운)을 배치 한 다음 2( 10/2 = 5, 다음 가장 가까운), 0( 10/4 = 2.5)을 배치합니까? 또는 : "값 v로부터의 거리의 평균 편차 감소"의 예를들 수 있습니까?
moraes

1
아니요, 나는 정반대입니다. 예를 들어, 평균 거리 2,5가 v = 4, 2, 1 및 싱글 톤 3에서 가장 많이 벗어나기 때문에 위치 결정 순서는 먼저 O입니다. 이 전략에 대한 설명의 일부?
babou

아냐 괜찮아 이 아이디어를 따라 뭔가를 시도하고 다시보고합니다.
moraes

1

파티에 늦었을 것 같지만, 누군가 다시이 문제에 부딪 치면 게시 할 수 있습니다. 내 솔루션은 @babou의 플러스와 비슷합니다. 오늘 초, 임베디드 시스템에서 스케줄링 문제가 발생하여이 스레드로 연결되었습니다. C에서 내 문제에 맞는 구현이 있지만 여기에 파이썬에서보다 일반적인 솔루션을 게시 할 것이라고 생각했습니다 (C 버전은 내가 작고 고정 된 크기의 스택으로 제한하고 메모리가 없다는 사실로 인해 복잡합니다. 할당, 그래서 나는 전체 알고리즘을 제자리에서 수행합니다. 아래에 사용되는 앤티 앨리어싱 기술은 2 비트 색상의 화면에 선을 그리는 데 사용할 수있는 방법입니다. 여기서 알고리즘은 특정 솔루션보다 Jim Mischel이 사용하는 입력에 대한 표준 편차의 합계를 사용하여 측정 할 때 낮은 점수 (즉, 더 나은)를 달성합니다.

def generate(item_counts):
'''item_counts is a list of counts of "types" of items. E.g., [3, 1, 0, 2] represents
   a list containing [1, 1, 1, 2, 4, 4] (3 types of items/distinct values). Generate
   a new list with evenly spaced values.'''
# Sort number of occurrences by decreasing value.
item_counts.sort(reverse=True)
# Count the total elements in the final list.
unplaced = sum(item_counts)
# Create the final list.
placements = [None] * unplaced

# For each type of item, place it into the list item_count times.
for item_type, item_count in enumerate(item_counts):
    # The number of times the item has already been placed
    instance = 0
    # Evenly divide the item amongst the remaining unused spaces, starting with
    # the first unused space encountered.
    # blank_count is the number of unused spaces seen so far and is reset for each
    # item type.
    blank_count = -1
    for position in range(len(placements)):
        if placements[position] is None:
            blank_count += 1
            # Use an anti-aliasing technique to prevent bunching of values.
            if blank_count * item_count // unplaced == instance:
                placements[position] = item_type
                instance += 1
    # Update the count of number of unplaced items.
    unplaced -= item_count

return placements

에 대한 결과

Input counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sum of stddev: 16.8 (vs. 22.3 via Jim Mischel)

Input of counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sum of stddev: 18.0 (vs. 19.3 via Jim Mischel)

@moraes에 의해 지정된 형식의 입력이 주어지면 Big Omega (n * log (n)) 비트의 메모리를 사용하여 O (n) 단계에서이 함수에 의해 사용 가능한 형식으로 변환 할 수 있습니다. 여기서 n은 항목 수입니다 ( 255 개의 요소가있는 목록에서는 반복 횟수와 병렬 배열을 유지하여 255 바이트를 초과 할 필요가 없습니다. 또는 O (1) 추가 메모리를 사용하여 한 쌍의 인플레 이스 정렬을 수행 할 수 있습니다.

추신

import numpy
import collections

def evaluate(l):
    '''Given a distribution solution, print the sum of stddevs for each type in the solution.'''
    l2 = l * 2
    distances = [None] * len(l)
    distance_dict = collections.defaultdict(list)
    for i in range(len(l)):
        distances[i] = l2.index(l[i], i + 1) - i
        distance_dict[l[i]].append(l2.index(l[i], i + 1) - i)

    keys = list(distance_dict.keys())
    keys.sort()
    score = 0
    # Calculate standard deviations for individual types.
    for key in keys:
        sc = numpy.std(distance_dict[key])
        score += sc
    print('Stddev sum: ', score)

편집 : 이 솔루션이 카운터 예제로 최적의 출력을 생성하지 못한다는 것을 알고 있습니다. [6, 2, 1]생산 의 입력 [0, 1, 0, 0, 2, 0, 0, 1, 0]; 더 나은 해결책은 [0, 0, 1, 0, 2, 0, 0, 1, 0]입니다.


코드 주석에서 알고리즘을 설명하고 프리앰블에서 알고리즘의 기초를 설명했습니다.
lungj

알고리즘의 아이디어와 알고리즘에 대한 간결한 의사 코드에 대한 독립적 인 설명을보고 싶었습니다. 현재 소개 텍스트에서 볼 수있는 것은 (1) 귀하의 접근 방식은 @babou와 유사하며 (2) 앤티 앨리어싱 기술을 사용합니다 (어쨌든). 또한, 여기에 모든 사람이 파이썬을 읽는 것은 아닙니다. 어쨌든, 그것은 오래된 대답이므로, 개선하고 싶지 않다면 이해합니다.하지만이 사이트에 대한 우리의 기대를 지적하고 있습니다. 미래에 대해 대답하는 경향이 있습니다.
DW

0

이 알고리즘은 정수 배열과 함께 작동하며 각 정수는 다른 범주를 나타냅니다. 각 범주마다 별도의 배열을 만듭니다. 예를 들어 시작 배열이 [1, 1, 1, 2, 2, 3]이면 [3], [2, 2], [1, 1, 1]의 세 배열이 만들어집니다.

거기에서 두 개의 가장 작은 배열 (이 예에서는 [3] 및 [2,2])을 재귀 적으로 결합하고, 대부분 숫자의 비율에 따라 더 작은 배열의 요소를 두 번째로 작은 배열로 배치합니다. 더 큰 카테고리와 더 작은 카테고리의 발생. 이 예에서는 [2,3,2]로 시작합니다. 그런 다음이 배열을 하나의 배열 만 남을 때까지 다음 배열로 결합 할 작은 배열로 사용합니다.

<?php
/**
 *This will separete the source array into separate arrays for each category
 */
function splitArrayByCategory($source) {

    //  index is the category, value is the tally
    $categoryCounts  = array_count_values($source);

    // Sort that list, keep index associations
    asort($categoryCounts);

    // build separate arrays for each category
    // index = order, smaller index = smaller category count
    // value = array of each category
    $separated = array();
    foreach ($categoryCounts as $category => $tally)
        $separated[] = array_fill(0, $tally, $category);

    return $separated;
}

/**
 * Will take the array of arrays generated by splitArrayByCategory, and merge
 * them together so categories are evenly distributed
 */
function mergeCategoryArrays($categoryArray) {

    // How many entries are there, for the smallest & second smallest categories
    $smallerCount = count($categoryArray[0]);
    $largerCount  = count($categoryArray[1]);

    // Used to determine how much space there should be between these two categories
    $space = $largerCount/$smallerCount;

    // Merge the array of the smallest category into the array of the second smallest
    foreach ($categoryArray[0] as $domIndex => $domain) {
        // Where should we splice the value into the second array?
        $location = floor($domIndex*$space+$domIndex+($space/2));
        // actually perform the splice
        array_splice($categoryArray[1], $location, 0, $domain);
    }

    // remove the smallest domain we just spliced into the second smallest
    unset($categoryArray[0]);

    // reset the indexes
    $categoryArray = array_values($categoryArray);

    // If we have more than one index left in the categoryArray (i.e. some things
    // still need to get merged), let's re-run this function,
    if (count($categoryArray)>1)
        $categoryArray = mergeCategoryArrays($categoryArray);

    return $categoryArray;
}

// The sample list we're working with.
// each integer represents a different category
$listSample = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,7,7,7];

// Split the sample list into separate arrays for each category
$listSplitByCategory = splitArrayByCategory($listSample);

// If there are not at least two categories, what's the point?
if (count($listSplitByCategory) < 2) throw new Exception("You need at least two categories");

// perform the actual distribution of categories.
$listEvenlyDistributed = mergeCategoryArrays($listSplitByCategory)[0];

// Display the array before and after the categories are evenly distributed
for ($i=0; $i<count($listSample); $i++) {
    print $listSample[$i].",";
    print $listEvenlyDistributed[$i]."\n";
}

2
이것은 코딩 사이트가 아닙니다. 코드 전용 답변을 게시하지 마십시오. 대신 답변 뒤에있는 아이디어를 설명하고 알고리즘에 대한 간결한 의사 코드를 제공하고자합니다.
DW

컴퓨터 과학에 오신 것을 환영합니다 ! 당신이 알지 못했거나 잠시 잊어 버린 경우를 대비하여 특정 언어로 코드를 읽는 것은 대개 코드가 우리 자신이 작성한 경우에도 우리가 가질 수있는 가장 어려운 작업 중 하나입니다. 이 코드는 느슨하게 작성된 의사 코드보다 훨씬 많은 작업을 나타낼 수 있지만이 사이트에서 실제 코드에 대해 대단히 감사하지 않는 이유 중 하나입니다. 물론, 즉시 실행하거나 반짝 거리는 모든 실제 작업 코드에 감사합니다.
Apass.Jack

설명이 있습니다. 주석이 달린 데모 코드에서; APL과 같은 일부 오래된 구문은 아니지만 의사 코드에 충분히 가까운 구문을 이해하기 쉽습니다. 내 설명이 모노 스페이스 글꼴이 아닌 경우 도움이됩니까?
vtim

예. 도움이됩니다. 모든 사람이 PHP를 읽는 것은 아니며, 모든 사람이 주석이 무엇인지 판단하거나 (아마도 짚맨 논쟁 일 수도 있음) 단순히 코드 블록을 읽고 해석하고 싶지는 않지만 맨 위에 포함 된 아이디어를 읽고 그것은 모든 것을 알려줍니다. 나에게서 +1 귀하의 코드는 깨끗하고 잘 문서화되어 있지만 Google은 단순히 사이트를 코딩하지 않으므로 텍스트 설명이 중요합니다. 편집 해 주셔서 감사합니다.
이블

-1

ANSI C 코드

이 코드는 방향 벡터 (v1, v2, ..., vi, ... vn)로 원점을 통과하는 n 차원 공간 (여기서 n은 범주 수)에서 직선을 상상하여 작동합니다. 여기서 vi는 카테고리의 아이템 i. 원점에서 시작하여 목표는 다음으로 가장 가까운 선을 찾는 것입니다. 예 [0 0 0 0 1 1 1 2 2 2 3]을 사용하여 결과 [0 1 2 0 3 1 0 2 0 1 2 0]을 생성합니다. Lungj의 예 [0 0 0 0 0 1 1 2]를 사용하여 [0 1 0 2 0 0 1 0]을 얻습니다. 이는 Lungj의 결과와 정확히 동일합니다.

이 알고리즘은 정수 산술 만 사용하고 각 점에서 선까지의 거리 사이의 델타 만 고려함으로써보다 효율적으로 만들어집니다.

# 정의 MAXCATEGORIES 100

int main () {int i = 0; int j = 0; int catsize = 0; int 벡터 [MAXCATEGORIES]; 시작점 [MAXCATEGORIES]; int 카테고리 = 0; int totalitems = 0; int best = 0; 긴 d2 = 0L; 긴 vp = 0L; 긴 v2 = 0L; 긴 델타 = 0L; 긴 베타 = 0L;

printf("Enter the size of each category (enter 0 to finish):\r\n");
do
{
    catsize = 0;
    #ifdef _MSVC_LANG
            scanf_s("%d", &catsize);
    #else
            scanf("%d", &catsize)
    #endif
    if (catsize > 0)
    {
        vector[categories] = catsize;
        totalitems += catsize;
        categories++;
    }
} while (catsize > 0);

for (i = 0; i < categories; i++)
{
    v2 += vector[i] * vector[i];
    point[i] = 0;
}

for (i = 0; i < totalitems; i++)
{
    for (j = 0; j < categories; j++)
    {
        delta = (2 * point[j] + 1)*v2 - (2 * vp + vector[j])*vector[j];
        if (j == 0 || delta < beta)
        {
            best = j;
            beta = delta;
        }
    }
    point[best]++;
    vp += vector[best];
    printf("%c ", best + '0');  // Change '0' to 'A' if you like letters instead
}
return 0;

}


1
사이트에 오신 것을 환영합니다! 서식을 지정하면 시스템에서 마크 업을 올바르게 수행 할 수 있도록 코드의 각 줄을 4 개의 공백으로 들여 쓰기해야합니다. 일반적으로 우리는 질문에 대한 답변으로 큰 코드 블록을 찾고 있지 않으며 특히 데이터 입력 루틴이 여기에 아무것도 추가하지 않습니다. 게시물 상단에 설명이 있지만 확장하여 코드를 줄이는 것이 좋습니다.
David Richerby

이것은 코딩 사이트가 아닙니다. 코드 전용 답변을 게시하지 마십시오. 대신 답변 뒤에있는 아이디어를 설명하고 알고리즘에 대한 간결한 의사 코드를 제공하고자합니다.
DW

-1

내 해결책 :

    vc = train['classes'].value_counts()
    vc = dict(sorted(vc.items()))
    df = pd.DataFrame()
    train['col_for_sort'] = 0.0

    i=1
    for k,v in vc.items():
        step = train.shape[0]/v
        indent = train.shape[0]/(v+1)
        df2 = train[train['classes'] == k].reset_index(drop=True)
        for j in range(0, v):
        df2.at[j, 'col_for_sort'] = indent + j*step + 0.0001*i   
    df= pd.concat([df2,df])
    i+=1

    train = df.sort_values('col_for_sort', ascending=False).reset_index(drop=True)
    del train['col_for_sort']

알고리즘을 설명하기 위해 의사 코드 (필수 주석 포함)를 사용하십시오.
xskxzr

이것은 코딩 사이트가 아닙니다. 코드 전용 답변을 게시하지 마십시오. 대신 답변 뒤에있는 아이디어를 설명하고 알고리즘에 대한 간결한 의사 코드를 제공하고자합니다.
DW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.