중심에 초점을 맞춘 난수 얻기


238

1-100 사이의 난수를 얻고 결과를 주로 40-60 범위 내에서 유지할 수 있습니까? 내 말은, 그 범위를 거의 벗어나지 않지만 주로 그 범위 내에 있기를 원합니다 ... JavaScript / jQuery로 가능합니까?

지금은 basic을 사용하고 Math.random() * 100 + 1있습니다.





20
나는이 질문이 어디로 가고 있는지 좋아하지만 더 구체적이어야한다고 생각합니다. Z- 분포 (벨 곡선), 삼각형 분포 또는 일종의 톱니 분포를 원하십니까? 내 의견으로는이 질문에 대답 할 수있는 여러 가지 가능성이 있습니다.
Patrick Roberts

12
이것은 자바 스크립트로 수행 할 수 있지만 jQuery와는 아무런 관련이 없습니다 ... :)
A. Wolff

답변:


397

가장 간단한 방법은 0-50에서 두 개의 난수를 생성하고 함께 더하는 것입니다.

이것은 두 개의 주사위 바이어스를 7쪽으로 굴리는 것과 같은 방식으로 50쪽으로 바이어스 된 분포를 제공합니다.

사실, @Falco가 제안한대로 많은 수의 "주사위" 를 사용하면 종 곡선에 더 가까운 근사치를 만들 수 있습니다.

function weightedRandom(max, numDice) {
    var num = 0;
    for (var i = 0; i < numDice; i++) {
        num += Math.random() * (max/numDice);
    }    
    return num;
}

가중 난수

JSFiddle : http://jsfiddle.net/797qhcza/1/


12
이것은 쉽고 빠른 솔루션입니다. 예를 들어 4 x (0-25)와 같이 더 많은 숫자를 추가하면 더 쉽게 가중치를 적용 할 수 있으며 분포를위한 멋진 벨 커브를 제공합니다!
팔코 12

8
이것은 환상적인 코드입니다. 나는 그것을 사랑한다고 생각합니다. 간단하고 빠르며 효율적입니다. 좋은 대답입니다. 이것을 게시 해 주셔서 감사합니다.
ctwheels

14
큰 대답이지만, 누군가 이것을 정규 분포를 생성하기 위해 사용하려는 경우, 그것은 비효율적입니다 (그리고 원하는 평균 및 표준 편차를 얻기 위해 그것을 변환해야합니다). 좀 더 효율적인 옵션은 Box-Muller 변환입니다. 약간의 수학을 알고 있으면 쉽게 구현하고 이해할 수 있습니다.
Brendon

1
@RaziShaban 매우 직관적입니다 : 최대 2 개 (뱀 눈만)를 추가하는 주사위 던지기 조합은 하나 뿐이지 만 6 개 (6-1, 5-2, 4-3, 3-)를 추가하는 6 가지 조합이 있습니다. 4, 2-5, 1-6). N면 주사위로 일반화하면 피크는 항상 N + 1입니다.
Barmar

2
@RaziShaban 랜덤 변수 연구 는 통계의 중심 부분입니다. 주사위를 늘리면 정규 분포에 접근한다는 사실이 유명한 Central Limit Theorem 입니다.
BlueRaja-대니 Pflughoeft

48

여기에는 구체적인 해결책을 제시하는 좋은 답변이 있습니다. 일반적인 해결책을 설명하겠습니다. 문제는:

  • 0과 1 사이에 다소 균일하게 분포 된 난수 의 출처가 있습니다 .
  • 다른 분포를 따르는 일련의 난수를 생성하고 싶습니다.

이 문제에 대한 일반적인 해결책은 운동하는 것입니다 분위수 기능을 원하는 분포를 한 다음 균일 한 소스의 출력에 분위수 기능을 적용 할 수 있습니다.

Quantile 함수는 원하는 분포 함수적분역수 입니다 . 분포 함수는 곡선의 한 부분 아래에있는 영역이 무작위로 선택된 항목이 그 부분에있을 확률과 같은 함수입니다.

여기에 그렇게하는 방법에 대한 예가 나와 있습니다.

http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/

코드는 C #에 있지만 원칙은 모든 언어에 적용됩니다. 솔루션을 JavaScript에 맞게 수정하는 것이 간단해야합니다.


2
나는이 접근법을 좋아한다. 가우시안 (및 기타 비정규) 배포판을 생성하는 자바 스크립트 라이브러리가 있다고 덧붙일 수 있습니다
Floris

36

숫자 등을 취하는 것은 효율적이지 않습니다. 0에서 100 사이의 난수를 사용하고 필요한 분포에 매핑하는 매핑을 사용해야합니다. 따라서 귀하의 경우 범위 중간에 가장 많은 값을 갖는 분포를 얻을 수 있습니다 .f(x)=-(1/25)x2+4x

분포


2
실제로 어떤 배포판이 필요한지 알 수 없습니다. "주로 40-60"은 저에게 종 곡선을 의미합니다.
Lefty

네, 맞습니다. 아마도 더 나은 매핑이 필요하지만, 사소한 일입니다
iCaramba

3
나는 이것이 나의 전문 분야가 아니기 때문에 당신의 말을 받아 들일 것입니다. 기능을 조정하고 새 곡선을 표시 할 수 있습니까?
Lefty

1
@Lefty- x0에서 100 사이의 단순화 된 벨 커브 ( 이 질문 에서 y = (Math.sin(2 * Math.PI * (x/100 - 1/4)) + 1) / 2
취함

@Sphinxxx 벨 커브가 아니라 사인 커브입니다. 벨 커브는 절대 x 축에 닿지 않습니다.
BlueRaja-대니 Pflughoeft

17

숫자가 "범위를 벗어남"으로 설정되도록 "기회"를 설정하는 것과 같은 것을 할 수 있습니다. 이 예에서 20 % 확률은 1-100, 그렇지 않으면 40-60입니다.

$(function () {
    $('button').click(function () {
        var outOfBoundsChance = .2;
        var num = 0;
        if (Math.random() <= outOfBoundsChance) {
            num = getRandomInt(1, 100);
        } else {
            num = getRandomInt(40, 60);
        }
        $('#out').text(num);
    });
    
    function getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<button>Generate</button>
<div id="out"></div>

바이올린 : http://jsfiddle.net/kbv39s9w/


5
더 자세한 통계를 가진 사람이 나를 바로 잡을 수는 있지만 OP가 찾고있는 것을 얻었 지 만 (투표 한 결과) 20 %의 범위 내에서 실제로 #을 선택하지는 않습니까? 이 솔루션에서 20 %의 시간은 1-100에서 40-60을 포함하여 #을 선택할 수 있습니다. 범위를 벗어난 #을 선택하는 것이 실제로 (0.2 * 0.8) 16 %가 아니거나 뭔가 빠졌습니까?
Josh

아니, 네 말이 맞아 그냥 내 말이다. 수정하겠습니다. 감사합니다!
Bitwise Creative

1
@ 조쉬-그것은 꽤 자리에 있습니다. 다음은 jsfiddle.net/v51z8sd5 모양의 간단한 증거입니다 . 경계에서 찾은 숫자의 백분율을 표시하고 약 0.16 (16 %)을 가리 킵니다.
트래비스 J

15

몇 년 전에이 문제를 해결해야했고 솔루션이 다른 답변보다 쉽습니다.

나는 경계 사이에 3 개의 무작위를 생성하고 평균을 냈습니다. 이것은 결과를 중심으로 끌어 당겨 지지만 사지에 완전히 도달 할 수있게합니다.


7
이것이 BlueRaja의 답변과 어떻게 더 낫거나 다른가요? 거기서 그는 (2,3, ... 원하는 숫자) 임의의 숫자의 합을 취하고 평균을 취합니다. BellFactor3 을 사용할 때 결과는 당신과 같습니다.
Floris

@floris 잘, 나는 c 언어의 언어로 코딩하지 않기 때문에 대답은 지금 다시 읽을 때까지 내 대답과 같은 일을하는 것처럼 보이지 않았습니다. 약간의 시행 착오로 내 방법을 만들었고 3 개의 난수가 올바른 숫자라는 것을 알았습니다. 또한 광산은 한 줄로 수행 할 수 있으며 여전히 이해하기 쉽습니다.
Lefty

2
정말? JS와 C 사이에 어떤 유사점이 없다고 생각하십니까? 글쎄, 그 언어들 중 어느 언어도 말할 수 없다고 말하거나, 나에게 익숙한 언어들과 비교할 때 Java도 모두 비슷하다.
Lefty

1
페어 포인트, 나는 실제로 내가 스스로 해결 한 것으로 타이틀에 끌 렸고 내가 한 방식을 자랑스럽게 생각했습니다. 다시 말하지만, 당신이 그렇게 말할 때까지 그것이 js 질문이라는 것을 알지 못했습니다. 운 좋게도, 내 기술은 언어에 의존하지 않으며 일부 사람들은 유용한 답변이라고 생각하는 것 같습니다.
Lefty

5
자바 스크립트는 사실 입니다 잘 아하지만 ...는 C 계열 언어.
Joren

14

그것은 보이는 바보하지만 당신은 랜드를 두 번 사용할 수 있습니다 :

var choice = Math.random() * 3;
var result;

if (choice < 2){
    result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
    result = Math.random() * 100 + 1;
}

11

물론 가능합니다. 무작위로 1-100을 만드십시오. 숫자가 <30이면 40-60 범위에서 생성되지 않으면 1-100 범위의 숫자를 생성하십시오.


11

이러한 난수를 생성하는 방법에는 여러 가지가 있습니다. 이를 수행하는 한 가지 방법은 여러 개의 균일 한 난수의 합을 계산하는 것입니다. 합산 한 난수와 범위는 최종 분포의 모양을 결정합니다.

숫자가 많을수록 중앙을 향해 더 많이 편향됩니다. 귀하의 질문 에 1의 난수합계를 사용하는 것이 이미 제안되었지만 눈에 띄는 것처럼 범위의 중심으로 편향되지 않습니다. 다른 답변은 2 개의 난수 또는 3 개의 난수합을 사용하여 제안 합니다 .

더 많은 난수의 합을 취하면 범위 중심으로 더 편향 될 수 있습니다. 극단적으로 당신은 각각 0 또는 1 인 99 개의 난수의 합을 취할 수 있습니다. 그것은 이항 분포입니다. 이항 분포는 어떤 의미에서 정규 분포의 개별 버전으로 볼 수 있습니다. 이론 상으로는 전체 범위를 다룰 수 있지만, 중심에 대한 편향이 너무 커서 끝점에 도달 할 것으로 예상해서는 안됩니다.

이 접근 방식은 원하는 바이어스를 조정할 수 있음을 의미합니다.


8

이와 같은 것을 사용하는 것은 어떻습니까?

var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
    var values = "";
    for(var i=0; i < loops; i++) {
        var numTries = tries;
        do {
            var num = Math.floor((Math.random() * 100) + 1);
            numTries--;
        }
        while((num < 40 || num >60) && numTries > 1)
        values += num + "<br/>";
    }
    return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>

내가 코딩 한 방식으로 몇 가지 변수를 설정할 수 있습니다 :
루프 = 결과 횟수
시도 = 함수가 while 루프를 통해 실행을 중단하기 전에 40-60 사이의 숫자를 얻으려고 시도하는 횟수

추가 보너스 : 그것은 동안 사용합니다 !!! 최고의 최고


8

당신은 사이에 임의의 값을 매핑하는 기능을 쓸 수 있습니다 [0, 1)[1, 100]무게에 따라. 이 예제를 고려하십시오.

중량 기준으로 0.0-1.0 내지 1-100

여기서 0.95값은 사이 의 값에 매핑됩니다 [61, 100].
실제로 우리는 .05 / .1 = 0.5에 매핑 할 [61, 100]때을 산출 81합니다.

기능은 다음과 같습니다.

/*
 * Function that returns a function that maps random number to value according to map of probability
 */
function createDistributionFunction(data) {
  // cache data + some pre-calculations
  var cache = [];
  var i;
  for (i = 0; i < data.length; i++) {
    cache[i] = {};
    cache[i].valueMin = data[i].values[0];
    cache[i].valueMax = data[i].values[1];
    cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
    cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
  }
  return function(random) {
    var value;
    for (i = 0; i < cache.length; i++) {
      // this maps random number to the bracket and the value inside that bracket
      if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
        value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
        value *= cache[i].valueMax - cache[i].valueMin + 1;
        value += cache[i].valueMin;
        return Math.floor(value);
      }
    }
  };
}

/*
 * Example usage
 */
var distributionFunction = createDistributionFunction([
  { weight: 0.1, values: [1, 40] },
  { weight: 0.8, values: [41, 60] },
  { weight: 0.1, values: [61, 100] }
]);

/*
 * Test the example and draw results using Google charts API
 */
function testAndDrawResult() {
  var counts = [];
  var i;
  var value;
  // run the function in a loop and count the number of occurrences of each value
  for (i = 0; i < 10000; i++) {
    value = distributionFunction(Math.random());
    counts[value] = (counts[value] || 0) + 1;
  }
  // convert results to datatable and display
  var data = new google.visualization.DataTable();
  data.addColumn("number", "Value");
  data.addColumn("number", "Count");
  for (value = 0; value < counts.length; value++) {
    if (counts[value] !== undefined) {
      data.addRow([value, counts[value]]);
    }
  }
  var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
  chart.draw(data);
}
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(testAndDrawResult);
<script src="https://www.google.com/jsapi"></script>
<div id="chart"></div>


7

다음은 3/4 40-60 및 1/4 범위 밖의 가중치 솔루션입니다.

function weighted() {

  var w = 4;

  // number 1 to w
  var r = Math.floor(Math.random() * w) + 1;

  if (r === 1) { // 1/w goes to outside 40-60
    var n = Math.floor(Math.random() * 80) + 1;
    if (n >= 40 && n <= 60) n += 40;
    return n
  }
  // w-1/w goes to 40-60 range.
  return Math.floor(Math.random() * 21) + 40;
}

function test() {
  var counts = [];

  for (var i = 0; i < 2000; i++) {
    var n = weighted();
    if (!counts[n]) counts[n] = 0;
    counts[n] ++;
  }
  var output = document.getElementById('output');
  var o = "";
  for (var i = 1; i <= 100; i++) {
    o += i + " - " + (counts[i] | 0) + "\n";
  }
  output.innerHTML = o;
}

test();
<pre id="output"></pre>


6

좋아, 그래서 나는 마지막 대답과 같은 느낌이 들기 때문에 다른 대답을 추가하기로 결정했습니다. 대부분의 대답은 벨 커브 형 결과 반환을 얻는 반 통계 방법을 사용합니다. 아래에서 제공하는 코드는 주사위를 굴릴 때와 같은 방식으로 작동합니다. 따라서 1 또는 99를 얻는 것이 가장 어렵지만 50을 얻는 것이 가장 쉽습니다.

var loops = 10; //Number of numbers generated
var min = 1,
    max = 50;
var div = $("#results").html(random());

function random() {
    var values = "";
    for (var i = 0; i < loops; i++) {
        var one = generate();
        var two = generate();
        var ans = one + two - 1;
        var num = values += ans + "<br/>";
    }
    return values;
}

function generate() {
    return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>


6

베타 배포판을 사용하는 것이 좋습니다 를 0-1 사이의 숫자를 생성 한 다음 확장하십시오. 매우 유연하고 다양한 형태의 분포를 만들 수 있습니다.

빠르고 더러운 샘플러는 다음과 같습니다.

rbeta = function(alpha, beta) {
 var a = 0   
 for(var i = 0; i < alpha; i++)   
    a -= Math.log(Math.random())

 var b = 0   
 for(var i = 0; i < beta; i++)   
    b -= Math.log(Math.random())

  return Math.ceil(100 * a / (a+b))
}

5
var randNum;
// generate random number from 1-5
var freq = Math.floor(Math.random() * (6 - 1) + 1);
// focus on 40-60 if the number is odd (1,3, or 5)
// this should happen %60 of the time
if (freq % 2){
    randNum = Math.floor(Math.random() * (60 - 40) + 40);
}
else {
    randNum = Math.floor(Math.random() * (100 - 1) + 1);
}

5

이 문제를 해결하는 가장 좋은 솔루션은 BlueRaja-Danny Pflughoeft가 제안한 솔루션 이지만 다소 빠르고 더 일반적인 솔루션도 언급 할 가치가 있다고 생각합니다.


두 가지 요구 사항을 충족시키는 난수 (문자열, 좌표 쌍 등)를 생성 해야하는 경우

  1. 결과 집합은 매우 작습니다. (16K 이하)
  2. 결과 집합은 신중합니다. (정수만 해당)

나는 일반적으로 요구 사항을 충족시키는 숫자 배열 (문자열, 좌표 쌍 등)을 만드는 것으로 시작합니다 (귀하의 경우 : 더 가능성이 높은 숫자를 여러 번 포함하는 숫자 배열). 그런 다음 해당 배열의 임의 항목을 선택하십시오. 이런 식으로, 당신은 항목 당 한 번 비싼 랜덤 함수를 호출해야합니다.


1
선택 항목을 미리 채울 경우 나중에 선택 항목을 섞을 수도 있습니다. 그럼 당신은 당신이 부족 때까지 순서대로 그들을 잡을 수 있습니다. 목록 끝에 도달하면 다시 섞습니다.
Geobits

@Geobits 목록을 섞는 것은 훨씬 더 자원 집약적 인 작업이며 그 중 하나를 임의로 선택하는 것입니다. 목록을 예측할 수 있어야하는 경우에만 좋은 선택입니다.
mg30rg

1
그러나 매번이 아니라 목록의주기마다 한 번만 수행하십시오. 만약 당신이 이것을 전처리한다면 (어쨌든 전처리 단계가 있기 때문에, 괜찮습니다) 나중에 각 번호를 얻는 것이 매우 빠릅니다. 가동 중지 시간이있을 때마다 다시 섞거나 임의의 숫자가 필요하지 않다는 것을 알 수 있습니다. 대안으로 제공하는 것 모두 장점이 있습니다.
Geobits

@Geobits 당신이 당신의 방식으로 그것을 수행하면, "단일 확률"숫자는 "떨어지게"될 것이고 다시 채워질 때까지 그들은 결과적으로 올 수 없습니다. (예를 들어, 2 개의 오지 던지기를 시뮬레이션하면 2 번 이상 두 번 이상 획득 할 수있는 기회가 거의 없습니다.)
mg30rg

1
그것이 드문 응용 프로그램을 제외하고는 사용하지 않는 것이 훨씬 더 좋은 이유입니다.;)
Geobits

4

분포

 5% for [ 0,39]
90% for [40,59]
 5% for [60,99]

해결책

var f = Math.random();
if (f < 0.05) return random(0,39);
else if (f < 0.95) return random(40,59);
else return random(60,99);

일반 솔루션

random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);

function random_choose (collections,probabilities)
{
    var acc = 0.00;
    var r1 = Math.random();
    var r2 = Math.random();

    for (var i = 0; i < probabilities.length; i++)
    {
      acc += probabilities[i];
      if (r1 < acc)
        return collections[i][Math.floor(r2*collections[i].length)];
    }

    return (-1);
}

function series(min,max)
{
    var i = min; var s = [];
    while (s[s.length-1] < max) s[s.length]=i++;
    return s;
}

4

40-60 또는 1-100의 난수 생성 여부에 도우미 난수를 사용할 수 있습니다.

// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;

var focuse_on_center = ( (Math.random() * 100) < weight_percentage );

if(focuse_on_center)
{
	// generate a random number within the 40-60 range.
	alert (40 + Math.random() * 20 + 1);
}
else
{
	// generate a random number within the 1-100 range.
	alert (Math.random() * 100 + 1);
}


4

gaussian기능을 사용할 수 있으면 사용하십시오. 이 함수는 average 0sigma 1 .

이 수의 95 %가 안에 average +/- 2*sigma있습니다. 당신 average = 50, 그리고 sigma = 5이렇게

randomNumber = 50 + 5*gaussian()

3

가장 좋은 방법은 특정 숫자 집합에 균등하게 분포 된 난수를 생성 한 다음 0과 100 사이의 집합에 투영 기능을 적용하여 투영이 원하는 숫자를 칠 가능성이 더 높다는 것입니다.

일반적으로이를 달성하는 수학적 방법은 원하는 숫자의 확률 함수를 플로팅하는 것입니다. 우리는 종 곡선을 사용할 수 있지만, 더 쉬운 계산을 위해 뒤집힌 포물선으로 작업 해 봅시다.

뿌리가 기울이지 않고 0과 100이되도록 포물선을 만들어 봅시다. 우리는 다음 방정식을 얻습니다.

f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x

이제 0에서 100 사이의 곡선 아래의 모든 영역은 숫자를 생성하려는 첫 번째 세트를 나타냅니다. 거기에서 세대는 완전히 무작위입니다. 따라서 우리가해야 할 일은 첫 번째 세트의 경계를 찾는 것입니다.

하한은 물론 0입니다. 상한은 100에서의 함수의 적분입니다.

F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)

따라서 0에서 166,666 사이의 숫자를 생성해야합니다. 그런 다음 그 숫자를 가져 와서 두 번째 세트 (0에서 100 사이)로 투사하면됩니다.

우리가 생성 한 난수는 입력 x가 0에서 100 사이 인 포물선의 일부 정수라는 것을 알고 있습니다. 즉, 난수가 F (x)의 결과라고 가정하고 x를 풀기 만하면됩니다.

이 경우 F (x)는 3 차 방정식이며 형식 F(x) = ax^3 + bx^2 + cx + d = 0에서는 다음과 같은 내용이 적용됩니다.

a = -1/3
b = 50
c = 0
d = -1 * (your random number)

x에 대해이 문제를 해결하면 찾고있는 실제 임의의 숫자가 생성되며 [0, 100] 범위에 있고 가장자리보다 중앙에 가까울 가능성이 훨씬 높습니다.


3

이 답변은 정말 좋습니다 . 그러나 다른 상황에 대한 구현 지침을 게시하고 싶습니다 (JavaScript가 아니므로 이해하기를 바랍니다).


모든 범위에 대한 범위와 가중치가 있다고 가정하십시오.

ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}

초기 정적 정보는 캐시 될 수 있습니다.

  1. 모든 가중치의 합 (샘플에서 108)
  2. 범위 선택 경계. 기본적으로이 공식은 다음 Boundary[n] = Boundary[n - 1] + weigh[n - 1]과 같습니다 Boundary[0] = 0. 샘플은Boundary = {0, 1, 3, 103, 108}

숫자 생성 :

  1. N[0, 모든 가중치의 합) 범위에서 난수를 생성하십시오 .
  2. for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
  3. i범위를 잡고 해당 범위에서 난수를 생성 하십시오 .

성능 최적화에 대한 추가 참고 사항. 범위는 오름차순 또는 내림차순으로 정렬 할 필요가 없으므로 빠른 가중치 범위를 찾으려면 가중치가 가장 높은 범위 조회 범위가 먼저 시작되고 가중치가 가장 낮은 범위가 먼저 시작됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.