내가있어 등 소수, INT 두 번에 대한 다음과 같은 기능의 다른 버전을 구현해야합니다, 그래서 슬프게도, .NET의 제네릭은 숫자 형 contraint을 지원하지 않습니다 대답. 그것은 엣지 케이스와 관련이 있습니다.
다음은 그의 메소드를 테스트하는 데 사용한 코드입니다. 확장 메서드를 약간 변경하여 int[]
을 반환하고 decimal
대신 double
을 수락했습니다.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Random rand = new Random(1325165);
int maxValue = 100;
int numberOfBuckets = 100;
List<double> values = new List<double>();
for (int i = 0; i < 10000000; i++)
{
double value = rand.NextDouble() * (maxValue+1);
values.Add(value);
}
int[] bins = values.Bucketize(numberOfBuckets);
PointPairList points = new PointPairList();
for (int i = 0; i < numberOfBuckets; i++)
{
points.Add(i, bins[i]);
}
zedGraphControl1.GraphPane.AddBar("Random Points", points,Color.Black);
zedGraphControl1.GraphPane.YAxis.Title.Text = "Count";
zedGraphControl1.GraphPane.XAxis.Title.Text = "Value";
zedGraphControl1.AxisChange();
zedGraphControl1.Refresh();
}
}
public static class Extension
{
public static int[] Bucketize(this IEnumerable<double> source, int totalBuckets)
{
var min = source.Min();
var max = source.Max();
var buckets = new int[totalBuckets];
var bucketSize = (max - min)/totalBuckets;
foreach (var value in source)
{
int bucketIndex = 0;
if (bucketSize > 0.0)
{
bucketIndex = (int)((value - min)/bucketSize);
if (bucketIndex == totalBuckets)
{
bucketIndex--;
}
}
buckets[bucketIndex]++;
}
return buckets;
}
}
0에서 100 사이의 10,000,000 임의의 double 값을 사용할 때 모든 것이 잘 작동합니다. 각 버킷은 대략 동일한 수의 값을 가지므로 Random
은 정규 분포를 반환한다는 의미에서 의미가 있습니다.
하지만
double value = rand.Next(0, maxValue + 1);
에
double value = rand.NextDouble() * (maxValue+1);
에서 값 세대 라인을 변경하고 다음과 같은 결과가 두 번 카운트 마지막 버킷을 얻을 때.
값이 양동이의 경계 중 하나와 동일 할 때, 그것은 기록 된대로 코드가 잘못된 양동이에 가치를두고 나타납니다. 이 인공물은 임의의 숫자가 버킷의 경계와 같을 확률이 희박하고 명백하지 않기 때문에 무작위로 double
값으로 나타나지 않습니다.
내가 수정 한 방법은 버킷 경계의 어느 쪽이 포함 된 것인가를 배타적이라고 정의하는 것입니다.
생각해의
0< x <=1
1< x <=2
...
99< x <=100
대
0<= x <1
1<= x <2
...99<= x <100
한계 값과 정확히 일치하는 값이 있으면 메서드가 어떤 버킷을 넣을 지 모르기 때문에 경계를 포함 할 수 없습니다.
public enum BucketizeDirectionEnum
{
LowerBoundInclusive,
UpperBoundInclusive
}
public static int[] Bucketize(this IList<double> source, int totalBuckets, BucketizeDirectionEnum inclusivity = BucketizeDirectionEnum.UpperBoundInclusive)
{
var min = source.Min();
var max = source.Max();
var buckets = new int[totalBuckets];
var bucketSize = (max - min)/totalBuckets;
if (inclusivity == BucketizeDirectionEnum.LowerBoundInclusive)
{
foreach (var value in source)
{
int bucketIndex = (int)((value - min)/bucketSize);
if (bucketIndex == totalBuckets)
continue;
buckets[bucketIndex]++;
}
}
else
{
foreach (var value in source)
{
int bucketIndex = (int)Math.Ceiling((value - min)/bucketSize) - 1;
if (bucketIndex < 0)
continue;
buckets[bucketIndex]++;
}
}
return buckets;
}
유일한 문제는 현재의 입력 데이터 세트는 최소 및 최대 값들의 많은 경우, 비닝 방법은 그 값과 집합을 잘못 할 얻어진 그래프의 많은 제외 것이다.
나는 SAS 언어를 사용하여 거의 동일한 알고리즘을 작성했고 내 개발자가 C#으로 번역해야했습니다. 고마워. –
@Jake Pearson : System.Linq 네임 스페이스를 가져 오는 경우 최소 및 최대 값을 찾기 위해 첫 번째 foreach 루프가 필요하지 않습니다. 대신 다음과 같이 작성하십시오. min = source.Min(); 및 max = source.Max(). Im는 그것이 CPU에 얼마나 더 효과적인지 모르지만 읽기는 약간 적습니다. –
양호한 통화가 업데이트되었습니다. –