คำนวณคะแนนสุ่ม (พิกเซล) ภายในวงกลม (รูปภาพ)


19

ฉันมีรูปภาพที่มีวงกลมที่ตำแหน่งเฉพาะและเส้นผ่านศูนย์กลางเฉพาะ สิ่งที่ฉันต้องทำก็คือสามารถคำนวณคะแนนสุ่มภายในวงกลมจากนั้นจัดการพิกเซลที่บอกว่ามีความสัมพันธ์กับจุดนั้น ฉันมีรหัสต่อไปนี้แล้ว:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * ( Math.PI * 2 );
    var x = _originX + ( _radius * Math.Cos( angle ) );
    var y = _originY + ( _radius * Math.Sin( angle ) );
    return new Point( ( int )x, ( int )y );
}

และมันใช้งานได้ดีสำหรับการค้นหาจุดทั้งหมดที่เส้นรอบวงของวงกลม แต่ฉันต้องการจุดทั้งหมดจากที่ใดก็ได้ในวงกลม หากนี่ไม่สมเหตุสมผลโปรดแจ้งให้ฉันทราบและฉันจะพยายามอย่างดีที่สุดเพื่อชี้แจง


ตรวจสอบการอัปเดต
David Gouveia

3
คำถามที่ดีในแง่ที่ว่าการสร้างการกระจายแบบถ่วงน้ำหนักเป็นข้อผิดพลาดทั่วไป
Tim Holt

คำตอบ:


35

หากคุณต้องการวิธีการง่ายๆเพียงแค่ทำการสุ่มรัศมีด้วย:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var radius = _random.NextDouble() * _radius;
    var x = _originX + radius * Math.Cos(angle);
    var y = _originY + radius * Math.Sin(angle);
    return new Point((int)x,(int)y);
}

อย่างไรก็ตามผลคะแนนของคุณจะเข้มข้นไปที่ศูนย์กลางของวงกลมมากขึ้น:

ป้อนคำอธิบายรูปภาพที่นี่

เพื่อให้ได้ชุดการกระจายให้ทำการเปลี่ยนแปลงอัลกอริทึมต่อไปนี้:

var radius = Math.Sqrt(_random.NextDouble()) * _radius;

ซึ่งจะให้ผลลัพธ์ดังต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ลิงค์ต่อไปนี้: แม ธ เวิลด์ - ดิสก์จุดการหยิบสินค้า

และสุดท้ายนี่คือการสาธิต JsFiddleอย่างง่ายเมื่อเปรียบเทียบอัลกอริทึมทั้งสองเวอร์ชัน


1
คำตอบที่ยอดเยี่ยม เพียงสิ่งหนึ่งที่จะเพิ่ม: อย่าลืมที่จะเมล็ดเครื่องกำเนิดไฟฟ้าจำนวนสุ่ม :)
kevintodisco

Oop คุณเอาชนะได้พบ - ไม่เห็นโพสต์นี้เมื่อฉันโพสต์ของฉัน ไซต์วุลแฟรมเป็นทรัพยากรที่ยอดเยี่ยมสำหรับสิ่งนี้
Tim Holt

1
@TimHolt เกิดขึ้นตลอดเวลา :)
David Gouveia

นี่สมมติว่าศูนย์กลางวงกลมอยู่ที่ 0,0 หรือไม่?
jjxtra

@PsychoDad ศูนย์กลางของวงกลมจะเป็น (_originX, _originY)
David Gouveia

5

อย่าใช้แค่สุ่ม r และ theta! สิ่งนี้สร้างการกระจายน้ำหนักแบบมีจุดมากขึ้นในศูนย์ หน้านี้แสดงภาพได้ดี ...

http://mathworld.wolfram.com/DiskPointPicking.html

นี่คือวิธีการที่สร้างการกระจายแบบไม่ถ่วงน้ำหนัก ...

var r = rand(0,1)
var theta = rand(0,360)

var x = sqrt(r) * cos(theta)
var y = sqrt(r) * sin(theta)

โอ๊ะโอของคำตอบที่เลือก: P
Tim Holt

ฉันสับสนเพราะคุณบอกว่าห้ามใช้การสุ่ม r และทีต้าเพราะมันสร้างการแจกแจงแบบถ่วงน้ำหนักแล้วโค้ดที่คุณแสดงว่าคุณอ้างว่าสร้างการแจกแจงแบบไม่ถ่วงน้ำหนักสร้าง r ภายในช่วง [0,1] คุณตั้งใจจะสแควร์รูทของตัวเลขสุ่มหรือไม่?
PeteUK

ใช่ทำรากที่สองของรัศมี (ตราบเท่าที่มันเป็น 0-1) ลดความเข้มข้นที่ไม่คาดคิดของจุดที่อยู่ตรงกลาง ดูลิงก์ Wolfram ที่ฉันโพสต์ซึ่งแสดงให้เห็นและอธิบายกับคณิตศาสตร์ได้ดีกว่าที่ฉันสามารถทำได้
ทิมโฮลท์

ความผิดฉันเอง. ฉันเห็นคุณทำ sqrt (r) เมื่อคำนวณ x และ y
PeteUK

4

คุณครึ่งทางแล้ว นอกเหนือจากการสร้างมุมสุ่มเพียงแค่สร้างระยะทางแบบสุ่มน้อยกว่าหรือเท่ากับรัศมีซึ่งมีน้ำหนักถ่วงน้ำหนักเพื่อให้คุณได้การกระจายแบบสม่ำเสมอ:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var distance = Math.Sqrt(_random.NextDouble()) * _radius;
    var x = _originX + (distance * Math.Cos(angle));
    var y = _originY + (distance * Math.Sin(angle));
    return new Point((int)x, (int)y);
}

ตอนนี้คุณกำลังคิดกับขั้วโลก

คุณยังสามารถถ่วงน้ำหนักระยะทางได้เช่นกันเพื่อหลีกเลี่ยงการรากที่สอง:

var distance = _random.NextDouble() + _random.NextDouble();
distance = (distance <= 1 ? distance : 2 - distance) * _radius;

โอเคดังนั้นเราจึงได้คำตอบที่แน่นอนภายในไม่กี่วินาทีของความแตกต่าง ตอนนี้คืออะไร :)
David Gouveia

@DavidGouveia เราทั้งสองได้รับ upvotes เพราะถูกต้อง ทุกคนชนะ! : D
Jon Purdy

ทั้งสองตอบคำชื่นชมอย่างมาก (และลิงค์ด้วย!) ผู้ชายฉันเป็นคนงี่เง่าที่ไม่ได้เห็นว่าตัวเอง -1 ให้ฉัน :( ขอบคุณอีกครั้งคุณทั้งสอง!
DMills

สิ่งนี้จะสร้างจุดสุ่ม แต่จะไม่กระจายอย่างสม่ำเสมอบนดิสก์ใช่ไหม แต่พวกเขาจะถูกถ่วงไปที่ศูนย์กลาง เพียงตรวจสอบฉันไม่ได้หายไปบางสิ่งบางอย่าง
PeteUK

1
@PeUK: คุณพูดถูกระยะทางควรมีน้ำหนัก ให้ฉันอัปเดต
Jon Purdy

3

หากประสิทธิภาพเป็นปัญหาวิธีแก้ไขปัญหาทางเลือกหนึ่งคือการสร้างตำแหน่งแบบสุ่มในกล่องที่มีความกว้าง / ความสูงของวงกลมแล้วโยนคะแนนใด ๆ ที่ไม่อยู่ในพื้นที่ของวงกลมออกไป

ข้อดีของวิธีนี้คือคุณไม่ได้ใช้ฟังก์ชั่น cos / sin / sqrt ซึ่งขึ้นอยู่กับแพลตฟอร์มของคุณอาจเป็นการประหยัดความเร็วขนาดใหญ่

var x = _random.NextDouble();
var y = _random.NextDouble();
if (x*x + y*y < 1.0f)
{
    // we have a usable point inside a circle
    x = x * diameter - _radius + _OriginX;
    y = y * diameter - _radius + _OriginY;
    // use the point(x,y)
}

ฉันจะลองดูและดูว่ามันเร่งความเร็วขึ้นไหม ฉันไม่แน่ใจว่าฉันมีปัญหาด้านประสิทธิภาพ แต่ฉันจะลองทำยังไงดีขอบคุณ!
DMills

0

ฉันใช้วิธีการอย่างใดอย่างหนึ่งในความคิดเห็นที่ระบุไว้และขยายฟังก์ชันการทำงานเพื่อสร้างระบบการสร้างจุดที่มีรูปทรงโดนัท

            private Vector2 CalculatePosition()
            {
                double angle = _rnd.NextDouble() * Math.PI * 2;
                double radius = InnerCircleRadius + (Math.Sqrt(_rnd.NextDouble()) * (OuterCircleRadius - InnerCircleRadius));
                double x = (_context.ScreenLayout.Width * 0.5f) + (radius * Math.Cos(angle));
                double y = (_context.ScreenLayout.Height * 0.5f) + (radius * Math.Sin(angle));
                return new Vector2((int)x, (int)y);
            }

มันเป็นวิธีการที่คล้ายกันดังกล่าวข้างต้น แต่ให้ผลลัพธ์ที่แตกต่าง ส่วนด้านในของวงกลมจะเว้นว่างไว้โดยไม่มีจุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.