ค่าเฉลี่ยของจำนวนเต็มยาว 3 ตัว


103

ฉันมีเลขจำนวนเต็มขนาดใหญ่มาก 3 ตัว

long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;

ฉันต้องการคำนวณค่าเฉลี่ยที่ถูกตัดทอน คาดว่าค่าเฉลี่ยซึ่งเป็นlong.MaxValue - 19223372036854775806

เป็นไปไม่ได้ที่จะคำนวณเป็น:

long avg = (x + y + z) / 3; // 3074457345618258600

หมายเหตุ: ฉันอ่านคำถามเหล่านี้ทั้งหมดเกี่ยวกับค่าเฉลี่ยของตัวเลข 2 ตัว แต่ฉันไม่เห็นว่าเทคนิคนั้นสามารถใช้กับค่าเฉลี่ยของตัวเลข 3 ตัวได้อย่างไร

มันจะง่ายมากกับการใช้งานBigIntegerแต่สมมติว่าฉันไม่สามารถใช้งานได้

BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806

ถ้าฉันแปลงเป็นdoubleแน่นอนฉันสูญเสียความแม่นยำ:

double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000

ถ้าฉันแปลงเป็นdecimalมันใช้งานได้ แต่สมมติว่าฉันไม่สามารถใช้มันได้

decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806

คำถาม:มีวิธีคำนวณค่าเฉลี่ยที่ถูกตัดทอนของจำนวนเต็ม 3 จำนวนที่มีขนาดใหญ่มากโดยใช้longประเภทหรือไม่? อย่าถือว่าคำถามนั้นเป็น C # - เฉพาะฉันแค่ให้ตัวอย่างใน C # ง่ายกว่า


1
ทำไมไม่คำนวณค่าเฉลี่ยความแตกต่างโดยรวมและแทนที่จากค่าสูงสุด
Andreas Niedermair

6
@AndreasNiedermair จะไม่ทำงานในกรณีที่ฉันมีlong.MinValueและlong.MaxValueอยู่ในค่าต่างๆ
Ulugbek Umirov

จับดีแน่นอน :)
Andreas Niedermair

แน่ใจหรือว่าเราต้องกังวลเกี่ยวกับเรื่องนี้กรอบงานนี้ไม่ควรจัดการ?
Bolu

11
มีเหตุผลที่แท้จริงBigIntegerหรือdecimalไม่หรือเป็นเพียงเพราะการทำให้ยากนี้?
jpmc26

คำตอบ:


142

รหัสนี้จะใช้งานได้ แต่ไม่สวยเท่าไหร่

ก่อนอื่นจะแบ่งทั้งสามค่า (มันจะเป็นพื้นของค่าดังนั้นคุณจะ 'สูญเสีย' ส่วนที่เหลือไป) จากนั้นหารส่วนที่เหลือ:

long n = x / 3
         + y / 3
         + z / 3
         + ( x % 3
             + y % 3
             + z % 3
           ) / 3

โปรดทราบว่าตัวอย่างข้างต้นทำงานไม่ถูกต้องเสมอไปเมื่อมีค่าลบอย่างน้อยหนึ่งค่า

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

ขอบคุณคำตอบและความคิดเห็นของUlugbek Umirov , James S , KevinZ , Marc van Leeuwen , gnasher729นี่คือทางออกปัจจุบัน:

static long CalculateAverage(long x, long y, long z)
{
    return (x % 3 + y % 3 + z % 3 + 6) / 3 - 2
            + x / 3 + y / 3 + z / 3;
}

static long CalculateAverage(params long[] arr)
{
    int count = arr.Length;
    return (arr.Sum(n => n % count) + count * (count - 1)) / count - (count - 1)
           + arr.Sum(n => n / count);
}

3
@DavidG เลขที่ในคณิตศาสตร์(x + y + z) / 3 = x / 3 + y / 3 + z / 3.
Kris Vandermotten

4
ฉันใช้ Z3 เพื่อพิสูจน์ความถูกต้องนี้สำหรับการนับตัวแปรทั้งหมดระหว่าง 1 ถึง 5
usr

5
แน่นอนว่าสิ่งนี้ดูเหมือนจะใช้งานได้ แต่วิธีการตัดทอนจำนวนเต็มจะทำให้คุณสับสน f(1,1,2) == 1ในขณะที่f(-2,-2,8) == 2
KevinZ

11
โปรดทราบว่าเนื่องจากความหมายของการทำงานของโมดูโลที่สมองได้รับความเสียหายสิ่งนี้สามารถให้ผลลัพธ์ที่ไม่ตรงกันคือปัดเศษขึ้นแทนที่จะเป็นลงหากอนุญาตให้ใช้ค่าเชิงลบสำหรับตัวแปรได้ ตัวอย่างเช่นถ้า x, y เป็นผลคูณบวกของ 3 และ z คือ -2 คุณจะได้รับ(x+y)/3ซึ่งมากเกินไป
Marc van Leeuwen

6
@KevinZ: ... ซึ่งผลกระทบนั้นจะต้องถูกยกเลิกโดยโปรแกรมเมอร์ที่ไม่เคยต้องการพฤติกรรมแบบกรณีพิเศษตั้งแต่แรก การให้โปรแกรมเมอร์ระบุโมดูลัสแทนที่จะต้องได้มาจากส่วนที่เหลือซึ่งคอมไพเลอร์อาจได้มาจากโมดูลัสดูเหมือนจะมีประโยชน์
supercat

26

NB - Patrick ได้ให้คำตอบที่ยอดเยี่ยมแล้ว เมื่อขยายสิ่งนี้คุณสามารถสร้างเวอร์ชันทั่วไปสำหรับจำนวนเต็มจำนวนเท่าใดก็ได้ดังนี้:

long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;

long[] arr = { x, y, z };
var avg = arr.Select(i => i / arr.Length).Sum() 
        + arr.Select(i => i % arr.Length).Sum() / arr.Length;

1
สิ่งนี้จะไม่เกิดขึ้นlongแต่สำหรับประเภทเล็ก ๆ โปรดทราบว่าผลรวมที่สองสามารถล้นได้
user541686

7

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

public class int128_t {
    private int H;
    private long L;

    public int128_t(int h, long l)
    {
        H = h;
        L = l;
    }

    public int128_t add(int128_t a)
    {
        int128_t s;
        s.L = L + a.L;
        s.H = H + a.H + (s.L < a.L);
        return b;
    }

    private int128_t rshift2()  // right shift 2
    {
        int128_t r;
        r.H = H >> 2;
        r.L = (L >> 2) | ((H & 0x03) << 62);
        return r;
    }

    public int128_t divideby3()
    {
        int128_t sum = {0, 0}, num = new int128_t(H, L);
        while (num.H || num.L > 3)
        {
            int128_t n_sar2 = num.rshift2();
            sum = add(n_sar2, sum);
            num = add(n_sar2, new int128_t(0, num.L & 3));
        }

        if (num.H == 0 && num.L == 3)
        {
            // sum = add(sum, 1);
            sum.L++;
            if (sum.L == 0) sum.H++;
        }
        return sum; 
    }
};

int128_t t = new int128_t(0, x);
t = t.add(new int128_t(0, y));
t = t.add(new int128_t(0, z));
t = t.divideby3();
long average = t.L;

ใน C / C ++ บนแพลตฟอร์ม 64 บิตนั้นง่ายกว่ามากด้วย __int128

int64_t average = ((__int128)x + y + z)/3;

2
ฉันขอแนะนำว่าวิธีที่ดีในการหารค่าที่ไม่ได้ลงนาม 32 บิตด้วย 3 คือการคูณด้วย 0x55555555L เพิ่ม 0x55555555 และเลื่อนไปทางขวาด้วย 32 วิธีการหารด้วย 3 ของคุณเมื่อเปรียบเทียบแล้วดูเหมือนว่าจะต้องใช้ขั้นตอนที่ไม่ต่อเนื่องหลายขั้นตอน
supercat

@supercat ใช่ฉันรู้วิธีนั้น วิธีที่แฮ็กเกอร์พอใจนั้นถูกต้องกว่า แต่ฉันจะนำไปใช้อีกครั้ง
phuclv

ฉันไม่แน่ใจว่า "ถูกต้องกว่า" หมายถึงอะไร การคูณซึ่งกันและกันในหลาย ๆ กรณีสามารถให้ค่าที่แน่นอนได้โดยตรงหรือมิฉะนั้นจะให้ค่าที่สามารถปรับแต่งได้ในหนึ่งหรือสองขั้นตอน BTW ฉันคิดว่าฉันควรจะแนะนำให้คูณด้วย 0x55555556 ซึ่งจะได้ผลลัพธ์ที่แน่นอนโดยไม่ต้อง "เพิ่ม" นอกจากนี้เงื่อนไขการวนซ้ำของคุณถูกต้องหรือไม่? อะไรปรับเปลี่ยน H และ L ในลูป
supercat

อนึ่งแม้ว่าหนึ่งไม่ได้มีฮาร์ดแวร์คูณหนึ่งสามารถได้อย่างรวดเร็วใกล้เคียงไม่ได้ลงนามผ่านทางx=y/3 x=y>>2; x+=x>>2; x+=x>>4; x+=x>>8; x+=x>>16; x+=x>>32;ผลลัพธ์จะใกล้เคียงกับ x มากและสามารถทำได้อย่างแม่นยำโดยการคำนวณdelta=y-x-x-x;และใช้การปรับแต่งxตามต้องการ
supercat

1
@ gnasher729 ฉันสงสัยว่ามันสามารถใช้การเพิ่มประสิทธิภาพนั้นในคอมพิวเตอร์ 32 บิตได้หรือไม่เนื่องจากมักไม่สามารถทำการคูณ 64x64 → 128 บิตได้
phuclv

7

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

สมมติว่า x คือสูงสุด y คือค่ามัธยฐาน z คือ min (ตามที่คุณมี) เราจะเรียกมันว่า max, median และ min

ตัวตรวจสอบเงื่อนไขเพิ่มตามความคิดเห็นของ @ UlugbekUmirov:

long tmp = median + ((min - median) / 2);            //Average of min 2 values
if (median > 0) tmp = median + ((max - median) / 2); //Average of max 2 values
long mean;
if (min > 0) {
    mean = min + ((tmp - min) * (2.0 / 3)); //Average of all 3 values
} else if (median > 0) {
    mean = min;
    while (mean != tmp) {
        mean += 2;
        tmp--;
    }
} else if (max > 0) {
    mean = max;
    while (mean != tmp) {
        mean--;
        tmp += 2;
    }
} else {
    mean = max + ((tmp - max) * (2.0 / 3));
}

2
ดูความคิดเห็นของ @ UlugbekUmirov: จะใช้ไม่ได้ในกรณีที่ฉันมีความยาว MinValue และยาว MaxValue ระหว่างค่าต่างๆ
Bolu

@Bolu ความคิดเห็นใช้ได้กับ long เท่านั้น MinValue ฉันจึงเพิ่มเงื่อนไขนี้เพื่อให้ใช้ได้กับกรณีของเรา
La-comadreja

คุณจะใช้ค่ามัธยฐานได้อย่างไรเมื่อยังไม่ได้เริ่มต้น
phuclv

@ LưuVĩnhPhúcค่ามัธยฐานคือค่าระหว่างค่าต่ำสุดและค่าสูงสุด
La-comadreja

1
ไม่(double)(2 / 3)เท่ากับ 0.0?
phuclv

5

เนื่องจาก C ใช้การหารแบบพื้นแทนการหารแบบยูคลิดจึงอาจง่ายกว่าในการคำนวณค่าเฉลี่ยที่ปัดเศษของค่าที่ไม่ได้ลงนามสามค่ามากกว่าค่าที่ลงนามสามค่า เพียงแค่เพิ่ม 0x8000000000000000UL ให้กับแต่ละหมายเลขก่อนที่จะนำค่าเฉลี่ยที่ไม่ได้ลงนามมาลบออกหลังจากรับผลลัพธ์แล้วใช้การร่ายที่ไม่ได้เลือกกลับเพื่อInt64ให้ได้ค่าเฉลี่ยที่มีลายเซ็น

ในการคำนวณค่าเฉลี่ยที่ไม่ได้ลงชื่อให้คำนวณผลรวมของ 32 บิตสูงสุดของค่าสามค่า จากนั้นคำนวณผลรวมของ 32 บิตด้านล่างของค่าทั้งสามค่าบวกผลรวมจากด้านบนบวกหนึ่ง [ค่าบวกคือการให้ผลลัพธ์ที่ปัดเศษ] ค่าเฉลี่ยจะเป็น 0x55555555 เท่าของผลรวมแรกบวกหนึ่งในสามของวินาที

ประสิทธิภาพของโปรเซสเซอร์ 32 บิตอาจได้รับการปรับปรุงโดยการสร้างค่า "sum" สามค่าซึ่งแต่ละค่ามีความยาว 32 บิตดังนั้นผลลัพธ์สุดท้ายคือ((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3; มันอาจจะได้รับการปรับปรุงเพิ่มเติมโดยการแทนที่sumL/3ด้วย((sumL * 0x55555556UL) >> 32)แม้ว่าตัวหลังจะขึ้นอยู่กับเครื่องมือเพิ่มประสิทธิภาพ JIT [มันอาจรู้วิธีแทนที่การหารด้วย 3 ด้วยการคูณและโค้ดของมันอาจมีประสิทธิภาพมากกว่าการดำเนินการคูณอย่างชัดเจน]


หลังจากเพิ่ม 0x8000000000000000UL แล้วการล้นมีผลต่อผลลัพธ์หรือไม่?
phuclv

@ LưuVĩnhPhúcไม่มีล้น ไปที่คำตอบของฉันสำหรับการนำไปใช้งาน การแบ่งออกเป็น 2 32 บิต int นั้นไม่จำเป็น
KevinZ

@KevinZ: การแยกแต่ละค่าออกเป็นส่วนบนและส่วนล่าง 32 บิตนั้นเร็วกว่าการแบ่งออกเป็นผลหารหารด้วยสามและส่วนที่เหลือ
supercat

1
@ LưuVĩnhPhúc: ไม่เหมือนกับค่าที่ลงนามซึ่งมีพฤติกรรมเหมือนตัวเลขและไม่ได้รับอนุญาตให้ล้นในโปรแกรม C ที่ถูกต้องโดยทั่วไปค่าที่ไม่ได้ลงชื่อจะทำงานเหมือนสมาชิกของวงแหวนพีชคณิตนามธรรมที่ห่อหุ้มดังนั้นความหมายของการตัดจึงถูกกำหนดไว้อย่างดี
supercat

1
ทูเพิลแทน -3, -2, -1 หลังจากเพิ่ม 0x8000U ให้กับแต่ละค่าแล้วค่าควรแบ่งครึ่ง: 7F + FF 7F + FE 7F + FD เพิ่มครึ่งบนและล่างโดยให้ผล 17D + 2FA บวกผลรวมครึ่งบนลงในผลรวมครึ่งล่างที่ได้ 477 คูณ 17D ด้วย 55 ให้ผล 7E81 หาร 477 ด้วยสามผลลัพธ์ 17D เพิ่ม 7E81 ถึง 17D ให้ผล 7FFE ลบ 8000 จากนั้นและได้ -2
supercat

5

การแก้ไขโซลูชันของPatrick Hofmanด้วยการแก้ไขของsupercatฉันให้สิ่งต่อไปนี้แก่คุณ:

static Int64 Avg3 ( Int64 x, Int64 y, Int64 z )
{
    UInt64 flag = 1ul << 63;
    UInt64 x_ = flag ^ (UInt64) x;
    UInt64 y_ = flag ^ (UInt64) y;
    UInt64 z_ = flag ^ (UInt64) z;
    UInt64 quotient = x_ / 3ul + y_ / 3ul + z_ / 3ul
        + ( x_ % 3ul + y_ % 3ul + z_ % 3ul ) / 3ul;
    return (Int64) (quotient ^ flag);
}

และกรณีองค์ประกอบ N:

static Int64 AvgN ( params Int64 [ ] args )
{
    UInt64 length = (UInt64) args.Length;
    UInt64 flag = 1ul << 63;
    UInt64 quotient_sum = 0;
    UInt64 remainder_sum = 0;
    foreach ( Int64 item in args )
    {
        UInt64 uitem = flag ^ (UInt64) item;
        quotient_sum += uitem / length;
        remainder_sum += uitem % length;
    }

    return (Int64) ( flag ^ ( quotient_sum + remainder_sum / length ) );
}

สิ่งนี้จะให้ค่าพื้น () ของค่าเฉลี่ยเสมอและกำจัดขอบที่เป็นไปได้ทุกกรณี


1
ฉันแปลรหัส AvgN เป็น Z3 และพิสูจน์ว่าสิ่งนี้ถูกต้องสำหรับขนาดอินพุตที่เหมาะสมทั้งหมด (เช่น 1 <= args.Length <= 5 และขนาด bitvector เป็น 6) คำตอบนี้ถูกต้อง
usr

คำตอบที่ยอดเยี่ยมเควิน ขอบคุณสำหรับการสนับสนุนของคุณ! meta.stackoverflow.com/a/303292/993547
Patrick Hofman

4

คุณสามารถใช้ความจริงที่ว่าคุณสามารถเขียนแต่ละตัวเลขที่เป็นy = ax + bที่xเป็นค่าคงที่ แต่ละอันaจะเป็นy / x(ส่วนจำนวนเต็มของการหารนั้น) แต่ละ b จะเป็นy % x(ส่วนที่เหลือ / โมดูโลของการหารนั้น) หากคุณเลือกค่าคงที่นี้ด้วยวิธีที่ชาญฉลาดเช่นโดยการเลือกรากที่สองของจำนวนสูงสุดเป็นค่าคงที่คุณจะได้ค่าเฉลี่ยของxตัวเลขโดยไม่ต้องมีปัญหากับการล้น

ค่าเฉลี่ยของรายการตัวเลขโดยพลการสามารถพบได้โดยการค้นหา:

( ( sum( all A's ) / length ) * constant ) + 
( ( sum( all A's ) % length ) * constant / length) +
( ( sum( all B's ) / length )

โดยที่%หมายถึงโมดูโลและ/หมายถึงส่วน 'ทั้งหมด' ของการหาร

โปรแกรมจะมีลักษณะดังนี้:

class Program
{
    static void Main()
    {
        List<long> list = new List<long>();
        list.Add( long.MaxValue );
        list.Add( long.MaxValue - 1 );
        list.Add( long.MaxValue - 2 );

        long sumA = 0, sumB = 0;
        long res1, res2, res3;
        //You should calculate the following dynamically
        long constant = 1753413056;

        foreach (long num in list)
        {
            sumA += num / constant;
            sumB += num % constant;
        }

        res1 = (sumA / list.Count) * constant;
        res2 = ((sumA % list.Count) * constant) / list.Count;
        res3 = sumB / list.Count;

        Console.WriteLine( res1 + res2 + res3 );
    }
}

4

ถ้าคุณรู้ว่าคุณมีค่า N คุณสามารถหารแต่ละค่าด้วย N แล้วรวมเข้าด้วยกันได้หรือไม่?

long GetAverage(long* arrayVals, int n)
{
    long avg = 0;
    long rem = 0;

    for(int i=0; i<n; ++i)
    {
        avg += arrayVals[i] / n;
        rem += arrayVals[i] % n;
    }

    return avg + (rem / n);
}

นี่เป็นเช่นเดียวกับวิธีแก้ปัญหาของ Patrick Hofman ถ้าไม่ถูกต้องน้อยกว่ารุ่นสุดท้าย
phuclv

2

ฉันยังลองและหาวิธีแก้ปัญหาที่เร็วกว่า (แม้ว่าจะมีเพียงปัจจัยประมาณ 3/4) มันใช้การหารเดียว

public static long avg(long a, long b, long c) {
    final long quarterSum = (a>>2) + (b>>2) + (c>>2);
    final long lowSum = (a&3) + (b&3) + (c&3);
    final long twelfth = quarterSum / 3;
    final long quarterRemainder = quarterSum - 3*twelfth;
    final long adjustment = smallDiv3(lowSum + 4*quarterRemainder);
    return 4*twelfth + adjustment;
}

โดยที่การsmallDiv3หารด้วย 3 โดยใช้การคูณและทำงานสำหรับอาร์กิวเมนต์ขนาดเล็กเท่านั้น

private static long smallDiv3(long n) {
    assert -30 <= n && n <= 30;
    // Constants found rather experimentally.
    return (64/3*n + 10) >> 6;
}

นี่คือรหัสทั้งหมดรวมถึงการทดสอบและเกณฑ์มาตรฐานผลลัพธ์ไม่น่าประทับใจเท่าไหร่


1

ฟังก์ชันนี้จะคำนวณผลลัพธ์เป็นสองส่วน ควรสรุปให้ชัดเจนกับตัวหารและขนาดคำอื่น ๆ

มันทำงานโดยการคำนวณผลลัพธ์การเพิ่มคำสองคำจากนั้นทำการหาร

Int64 average(Int64 a, Int64 b, Int64 c) {
    // constants: 0x10000000000000000 div/mod 3
    const Int64 hdiv3 = UInt64(-3) / 3 + 1;
    const Int64 hmod3 = UInt64(-3) % 3;

    // compute the signed double-word addition result in hi:lo
    UInt64 lo = a; Int64 hi = a>=0 ? 0 : -1;
    lo += b; hi += b>=0 ? lo<b : -(lo>=UInt64(b));
    lo += c; hi += c>=0 ? lo<c : -(lo>=UInt64(c));

    // divide, do a correction when high/low modulos add up
    return hi>=0 ? lo/3 + hi*hdiv3 + (lo%3 + hi*hmod3)/3
                 : lo/3+1 + hi*hdiv3 + Int64(lo%3-3 + hi*hmod3)/3;
}

0

คณิตศาสตร์

(x + y + z) / 3 = x/3 + y/3 + z/3

(a[1] + a[2] + .. + a[k]) / k = a[1]/k + a[2]/k + .. + a[k]/k

รหัส

long calculateAverage (long a [])
{
    double average = 0;

    foreach (long x in a)
        average += (Convert.ToDouble(x)/Convert.ToDouble(a.Length));

    return Convert.ToInt64(Math.Round(average));
}

long calculateAverage_Safe (long a [])
{
    double average = 0;
    double b = 0;

    foreach (long x in a)
    {
        b = (Convert.ToDouble(x)/Convert.ToDouble(a.Length));

        if (b >= (Convert.ToDouble(long.MaxValue)-average))
            throw new OverflowException ();

        average += b;
    }

    return Convert.ToInt64(Math.Round(average));
}

สำหรับชุดของ{1,2,3}คำตอบคือแต่รหัสของคุณจะกลับมา2 1
Ulugbek Umirov

@UlugbekUmirov รหัสคงที่ควรใช้สองประเภทในการประมวลผล
Khaled.K

1
นั่นคือสิ่งที่ฉันต้องการหลีกเลี่ยง - การใช้งานdoubleเนื่องจากเราจะสูญเสียความแม่นยำในกรณีเช่นนี้
Ulugbek Umirov

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