다음과 같이 이미지 파일을 만들려고했습니다.
uint8_t raw_r[pixel_width][pixel_height];
uint8_t raw_g[pixel_width][pixel_height];
uint8_t raw_b[pixel_width][pixel_height];
uint8_t blue(uint32_t x, uint32_t y)
{
return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
return (rand()%2)? (y-x)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
for (y=0; y<pixel_height; ++y)
{
for (x=0; x<pixel_width; ++x)
{
raw_b[x][y]=blue(x, y);
raw_g[x][y]=green(x, y);
raw_r[x][y]=red(x, y);
}
}
나는 임의의 것을 얻을 것으로 예상했다 (흰색 소음). 그러나 결과는 흥미 롭습니다.
이유를 알고 있습니까?
편집하다
이제는와 관련이 없음이 분명합니다 rand()
.
이 코드를 사용해보십시오 :
for (x=0; x<pixel_width; ++x)
for (y=0; y<pixel_height; ++y)
{
r[x][y] = (x+y);
g[x][y] = (y-x);
/* b[x][y] = rand()%2? x : y; */
}
답변
나는 처음에 다른 모든 사람들과 같은 대답을하려고했고이 문제에 대한 답을 얻었습니다 rand()
. 그러나 나는 그렇게하는 것이 더 낫다고 생각하고 대신 수학이 실제로 생성하는 분포를 분석했습니다.
TL; DR : 보이는 패턴은 기본 난수 생성기와 아무 관련이 없으며 단순히 프로그램이 숫자를 조작하는 방식 때문입니다.
나는 모두 비슷하기 때문에 파란색 기능을 고수 할 것입니다.
uint8_t blue(uint32_t x, uint32_t y) {
return (rand() % 2) ? (x + y) % rand() :
((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
rand();
}
각각의 픽셀 값은 세 가지 기능들 중 하나로부터 선택된다 : (x + y) % rand()
, (x - y) % rand()
및 rand()
;
이들 각각에 의해 생성 된 이미지를 보자.
rand()
이것이 바로 잡음입니다. 이것을 “이미지 C”라고 부릅니다.
(x + y) % rand()
여기에서는 픽셀 좌표를 더하고 나머지를 난수로 나누지 않습니다. 이미지가 1024×1024 인 경우 합계는 [0-2046] 범위에 있습니다. 다이빙하는 난수는 [0, RAND_MAX] 범위에 있으며 RAND_MAX는 32k 이상이고 일부 시스템에서는 20 억입니다. 다른 말로하면, 나머지 16 분의 1만이 16에서 1의 확률로 존재합니다 (x + y)
. 따라서이 함수는 대부분 + x + y 방향으로 파란색이 증가하는 그래디언트를 생성합니다.
그러나을 반환하기 때문에 가장 낮은 8 비트 만 사용 uint8_t
하므로 너비가 256 픽셀 인 그라디언트 줄무늬가 나타납니다.
이것을 “이미지 A”라고 부릅니다.
(x - y) % rand()
여기에 비슷한 것이 있지만 빼기가 있습니다. x가 y보다 크면 이전 이미지와 비슷한 것이 있습니다. Y가 큰 경우 그러나, 결과는 때문에 매우 큰 숫자 x
와 y
부호가 (부정적인 결과는 부호없는 형식의 범위의 상단에 랩 어라운드는) 다음 % rand()
에서 차기 당신은 실제로 소음 얻을.
이것을 “이미지 B”라고 부릅니다.
최종 이미지의 각 픽셀은 기능 rand() % 2
및을 사용하여이 세 이미지 중 하나에서 가져옵니다 ((x * y % 1024) % rand()) % 2
. 이 중 첫 번째는 50 % 확률로 선택하여 읽을 수 있습니다 ( rand()
낮은 비트와 관련된 문제는 무시 함 ).
여기 어디의 근접 촬영입니다 rand() % 2
은 이미지 A가 선택 이 참 (흰색 픽셀) 인 .
두 번째 기능은 ((x * y % 1024) % rand()) % 2
다시 문제가 있습니다.rand()
나누고있는 것보다 (x * y % 1024)
최대 1023 인 문제를 가지고 있습니다. 그런 다음 (x*y%1024)%2
0과 1을 똑같이 자주 생성하지 않습니다. 홀수에 짝수를 곱한 값은 짝수입니다. 짝수를 곱한 짝수도 짝수입니다. 홀수에 홀수를 곱한 %2
값만 홀수이므로 시간의 3/4조차도 0의 3/4 시간을 생성합니다.
다음 ((x * y % 1024) % rand()) % 2
은 이미지 B를 선택할 수있는 위치의 근접 촬영입니다 . 두 좌표가 홀수 인 곳을 정확하게 선택합니다.
이미지 C를 선택할 수있는 곳은 다음과 같습니다.
마지막으로 이미지 B가 선택된 조건을 결합하십시오.
이미지 C가 선택된 위치 :
결과 조합은 다음과 같이 읽을 수 있습니다.
확률이 50 %이면 이미지 A의 픽셀을 사용합니다. 나머지 시간은 이미지 B와 이미지 C, 두 좌표가 홀수 인 B, 둘 중 하나가 짝수 인 C 사이에서 선택됩니다.
마지막으로 세 가지 색상에 대해 동일한 작업을 수행하지만 방향이 다르면 각 색상에서 패턴의 방향이 다르며보고있는 교차 스트립 또는 격자 패턴이 생성됩니다.
답변
코드에서 수행하는 많은 계산은 실제로 임의의 값으로 이어지지 않습니다. 당신이보고있는 날카로운 선은 x와 y 좌표의 상대 값이 서로 거래되는 장소에 해당하며, 그럴 때 근본적으로 다른 공식을 사용하고 있습니다. 예를 들어, 컴퓨팅 (x + y) % rand()
은 일반적으로 값을 돌려줍니다 x + y
. 왜냐하면 rand()
의지가 (보통)보다 훨씬 큰 숫자를 반환하기 때문 입니다. 위에서 본 것과 같은 멋진 패턴을 원하지만 여기저기서 약간의 무작위성을 던지면 작성한 코드를 계속 사용하십시오.x + y
RAND_MAX
일반적으로 상당히 큰 숫자 때문 입니다. 그런 의미에서, 물건을 생성하는 데 사용하는 알고리즘이 백색 잡음을 생성하는 데 편향되어 있지 않기 때문에 백색 잡음을 되 찾을 것으로 기 대해서는 안됩니다. 화이트 노이즈를 원하면 각 픽셀을rand()
또한 @ pm100이 주석에서 언급했듯이 rand
함수는 실제로 임의의 숫자를 반환하지 않으며 대신 의사 난수 함수를 사용하여 값을 생성합니다. rand
많은 시스템에서 기본 구현은 짧은 연속 버스트가 무작위로 나타날 수 있지만 실제로는 비 랜덤 인 숫자를 생성 하는 선형 합동 생성기 라고하는 의사 난수 생성기 유형을 사용합니다 . 예를 들어, 다음은 선형 합동 발생기로 선택한 공간의 임의의 점이 고정 된 수의 초평면으로 떨어지는 방법을 보여주는 Wikipedia의 애니메이션입니다.
x, y 및 z 좌표를 R, G 및 B 좌표 로 바꾸면 프로그램에서 생성되는 출력과 매우 유사합니다. 위에서 언급 한 다른 측면이 훨씬 더 두드러지기 때문에 이것이 아마도 핵심 문제가 아닌 것 같습니다.
고품질 난수를 찾으려면 고품질 난수를 사용해야합니다. C에서는 /dev/urandom/
리눅스와 같은 시스템 에서 바이트를 읽는 것을 고려할 수 있는데, 이는 상당히 균일 한 임의의 값을 제공합니다. C ++은 표준 라이브러리에 여러 가지 난수 생성 프리미티브가 있습니다.