파이썬 3.8.0 목록을 세트로 변환하면 결과 세트 순서 *는 사소한 방식으로 고도로 구조화됩니다. 이 구조는 의사 난수 목록에서 어떻게 추출됩니까?
실험 중 일부로 임의 세트를 생성하고 있습니다. 세트를 플로팅하면 갑자기 세트에서 예기치 않은 선형 구조가 나타나는 것을보고 놀랐습니다. 그래서 두 가지가 당혹 스럽습니다. 왜 세트 결과로 변환하면 순서가 *이 구조를 강조 표시합니까? 그리고 의사 난수 세트가 왜이 “숨겨진”구조를 갖는가?
코드:
X = [randrange(250) for i in range(30)]
print(X)
print(set(X))
예를 들어
[238, 202, 245, 94, 111, 106, 148, 164, 154, 113, 128, 10, 196, 141, 69, 38, 106, 8, 40, 53, 160, 87, 85, 13, 38, 147, 204, 50, 162, 91]
{128, 8, 10, 141, 13, 147, 148, 154, 160, 162, 164, 38, 40, 50, 53, 196, 69, 202, 204, 85, 87, 91, 94, 106, 238, 111, 113, 245}
위 목록의 플롯 **은 예상대로 상당히 무작위로 보입니다.
반면 (출력에서 순서대로) 세트를 플로팅하면 세트에 존재하는 구조가 나타납니다.
이 동작은 위의 코드에서 사용 된 250과 30의 값으로 내 컴퓨터에서 100 % 일관성을 유지합니다 (아래 예제 더). 사용한 예제는 체리 선택이 아니며 마지막으로 실행 한 것입니다. 이 값을 조정하면 구조가 약간 달라 지기도합니다 (예 : 2 대신 3 개의 산술 진행 ***의 하위 집합).
다른 사람의 기기에서도 재현 할 수 있습니까? 물론, 그러한 구조가 존재한다는 것은 그렇게 큰 의사 난수 생성을 나타내는 것으로 보이지만 이것은 집합으로 변환하는 것이 어떤 의미에서이 구조를 ‘추출’할 수 있는지를 설명하지는 않습니다. 내가 아는 한, 세트의 순서 (목록에서 변환 될 때)가 결정적이라고 공식적으로 보장하지는 않습니다 (그렇더라도 백그라운드에서 정교한 순서가 수행되지 않음). 어떻게 이런 일이 일어나고 있습니까?!
(*) : 알아, 세트가 정렬되지 않은 컬렉션,하지만 난이 말은 호출 할 때, 의미에서 “주문” print
문을 세트가 출력됩니다 일부 지속적으로 기본 설정 구조를 강조하기 위해.
(**) :이 도표 는 Wolfram Alpha에서 나왔습니다. 아래에 두 가지 예가 더 있습니다.
(***) : 난수 범위를 250에서 500으로 변경할 때 두 개의 플롯 :
답변
기본적으로 이것은 두 가지 때문입니다.
- 파이썬에서 세트는 사용하여 구현되는 해시 테이블을 ,
- 정수의 해시는 정수 자체입니다.
따라서 정수가 기본 배열에 나타나는 인덱스는 정수 값에 따라 결정되며 기본 배열의 길이는 모듈로입니다. 따라서 정수를 연속 범위에 넣으면 정수가 오름차순으로 유지되는 경향이 있습니다.
>>> list(set(range(10000))) == list(range(10000))
True # this can't be an accident!
연속 된 범위의 숫자가 모두 없으면 “기본 배열의 길이”모듈러스 부분이 작동합니다.
>>> r = range(0, 50, 4)
>>> set(r)
{0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28}
>>> sorted(r, key=lambda x: x % 32)
[0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28]
기본 배열의 길이와 요소 추가를위한 (결정적) 알고리즘을 알고 있으면 시퀀스를 예측할 수 있습니다. 이 경우 배열의 길이는 32이며 처음에 8 이고 요소가 추가되는 동안 4 배가 되기 때문 입니다.
(번호 52, 56 세트에 있지 않기 때문에) 범위가 두 시퀀스로 분할되며, 단부 근처 글 제외 0, 4, 8, ...
하고 32, 36, 40, ...
숫자 ‘값 자체 인 해시, 선택 모듈 (32)을 가지고 있기 때문에 교번 배열의 인덱스. 충돌이 있습니다. 예를 들어, 4와 36은 동일한 모듈로 32이지만, 4가 먼저 세트에 추가되었으므로 36은 다른 인덱스로 끝납니다.
이 순서에 대한 차트는 다음과 같습니다. 차트의 구조는 단계가 아닌 범위에서 임의로 숫자를 생성하기 때문에 소음이 적은 버전입니다.
인터리브 된 시퀀스의 수는 숫자가 샘플링되는 범위의 길이에 비례하여 세트의 크기에 따라 결정되는데, 그 이유는 범위의 길이가 해시 테이블의 기본 배열의 길이를 모듈로 “랩핑”하는 횟수를 결정하기 때문입니다. 여기에 세 가지 인터리브 시퀀스 예입니다 0, 6, 12, ...
, 66, 72, 78, ...
그리고 36, 42, 48, ...
:
>>> set(range(0, 90, 6))
{0, 66, 36, 6, 72, 42, 12, 78, 48, 18, 84, 54, 24, 60, 30}