จะปัดเศษผลลัพธ์ของการหารจำนวนเต็มได้อย่างไร


335

ฉันกำลังคิดถึงวิธีแสดงการควบคุมเลขหน้าโดยเฉพาะเมื่อใช้ภาษาเช่น C # หรือ Java

หากฉันมีรายการxที่ฉันต้องการแสดงในหน่วยของyต่อหน้าจะต้องมีกี่หน้า?


1
ฉันพลาดอะไรไปรึเปล่า? y / x + 1 ใช้งานได้ดี (หากคุณรู้ว่า / โอเปอเรเตอร์ปัดเศษลงเสมอ)
rikkit

51
@rikkit - ถ้า y และ x เท่ากัน y / x + 1 นั้นสูงเกินไป
เอียนเนลสัน

1
สำหรับทุกคนที่เพิ่งค้นพบสิ่งนี้คำตอบสำหรับคำถามที่ถูกล่อลวงนี้จะช่วยหลีกเลี่ยงการแปลงที่ไม่จำเป็นเป็นสองเท่าและหลีกเลี่ยงความกังวลล้นนอกเหนือจากการให้คำอธิบายที่ชัดเจน
ZX9

2
@IanNelson อื่น ๆ โดยทั่วไปถ้าxหารด้วยy, y/x + 1จะเป็นหนึ่งในที่สูงเกินไป
Ohad Schneider

1
@ ZX9 ไม่มันไม่ได้ช่วยอะไรมาก มันเป็นคำตอบเดียวกับ Ian Nelson ที่โพสต์ที่นี่
user247702

คำตอบ:


478

พบทางออกที่สง่างาม:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

ที่มา: การแปลงตัวเลข, Roland Backhouse, 2001


12
-1 เนื่องจากข้อผิดพลาดล้นที่ชี้โดยBrandon DuRette
finnw

30
นายชัดเจนกล่าวว่า: อย่าลืมตรวจสอบให้แน่ใจว่าระเบียนหน้าเพจไม่เป็นศูนย์
อดัมเจนต์

7
ทำได้ดีมากฉันไม่อยากจะเชื่อ C # ไม่มีจำนวนเต็มเพดาน
gosukiwi

2
ใช่ที่นี่ฉันอยู่ในช่วงกลางปี ​​2560 สะดุดคำตอบที่ยอดเยี่ยมนี้หลังจากลองวิธีการที่ซับซ้อนมากขึ้น
Mifo

1
สำหรับภาษาที่มีตัวดำเนินการหาร Euclidian ที่เหมาะสมเช่น Python วิธีการที่เรียบง่ายกว่าก็pageCount = -((-records) // recordsPerPage)คือ
supercat

194

การแปลงเป็นจุดลอยตัวและด้านหลังดูเหมือนว่าเป็นการเสียเวลาอย่างมากที่ระดับ CPU

ทางออกของ Ian Nelson:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

สามารถทำให้ง่ายขึ้นไปที่:

int pageCount = (records - 1) / recordsPerPage + 1;

AFAICS นี่ไม่มีข้อผิดพลาดล้นที่แบรนดอนดูเรตต์ชี้ให้เห็นและเนื่องจากมันใช้เพียงครั้งเดียวคุณไม่จำเป็นต้องจัดเก็บ recordsPerPage เป็นพิเศษหากมันมาจากฟังก์ชั่นราคาแพงเพื่อดึงค่าจากไฟล์ config หรือ บางสิ่งบางอย่าง

เช่นนี้อาจไม่มีประสิทธิภาพหาก config.fetch_value ใช้การค้นหาฐานข้อมูลหรือบางสิ่ง:

int pageCount = (records + config.fetch_value('records per page') - 1) / config.fetch_value('records per page');

สิ่งนี้สร้างตัวแปรที่คุณไม่ต้องการซึ่งอาจมีหน่วยความจำ (รอง) และพิมพ์เพียงมากเกินไป:

int recordsPerPage = config.fetch_value('records per page')
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

นี่คือทั้งหมดหนึ่งบรรทัดและดึงข้อมูลเพียงครั้งเดียว:

int pageCount = (records - 1) / config.fetch_value('records per page') + 1;

5
+1, ปัญหาการบันทึกศูนย์ยังคงส่งคืน 1 หน้านับเป็นประโยชน์จริง ๆ เพราะฉันยังต้องการหน้า 1 แสดงแถวตัวยึด / แถวปลอมของ "ไม่มีระเบียนที่ตรงกับเกณฑ์ของคุณ" ช่วยหลีกเลี่ยงปัญหา "จำนวนหน้า 0" ในสิ่งใดก็ตาม การควบคุมการแบ่งหน้าคุณใช้
Timothy Walters

27
โปรดระวังว่าโซลูชันทั้งสองจะไม่ส่งคืนหน้าเดียวกันนับสำหรับบันทึกศูนย์ เวอร์ชันที่เรียบง่ายนี้จะส่งคืน 1 pageCount สำหรับบันทึกศูนย์ในขณะที่รุ่น Roland Backhouse ส่งคืน 0 pageCount ไม่เป็นไรถ้านั่นคือสิ่งที่คุณต้องการ แต่สมการทั้งสองนั้นไม่เทียบเท่ากันเมื่อดำเนินการโดยการหารจำนวนเต็มของ C # / Java
เอียนเนลสัน

10
แก้ไขเล็ก ๆ เพื่อความชัดเจนสำหรับคนที่สแกนมันและ bodmas ที่หายไปเมื่อเปลี่ยนเป็น simpification จากวิธีแก้ปัญหาของเนลสัน (เหมือนที่ฉันทำครั้งแรก!) การทำให้เข้าใจง่ายด้วยวงเล็บคือ ... int pageCount = ((records - 1) / recordsPerPage) + 1;
เดฟ heywood

1
คุณควรเพิ่มวงเล็บให้เป็นเวอร์ชั่นที่เรียบง่ายเพื่อไม่ให้ขึ้นอยู่กับลำดับการทำงานเฉพาะ เช่น ((บันทึก - 1) / recordsPerPage) + 1
Martin

1
@Ian คำตอบนี้ไม่เคยกลับ 1 มันสามารถกลับ 0 ถ้า recordsPerPage ของคุณคือ "1" และมี 0 -1 / 1 + 1 = 0บันทึก: ในขณะที่ไม่ใช่เหตุการณ์ที่เกิดขึ้นทั่วไปเป็นสิ่งสำคัญที่ต้องจำไว้ว่าถ้าคุณอนุญาตให้ผู้ใช้ปรับขนาดหน้า ดังนั้นไม่อนุญาตให้ผู้ใช้มีขนาดหน้า 1 ตรวจสอบขนาดหน้าหรือทั้งสองอย่าง (อาจดีกว่าเพื่อหลีกเลี่ยงพฤติกรรมที่ไม่คาดคิด)
Michael

81

สำหรับ C # การแก้ปัญหาคือการแปลงค่าให้เป็นสองเท่า (เนื่องจาก Math.Ceiling ใช้ค่าเป็นสองเท่า):

int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);

ใน java คุณควรทำสิ่งเดียวกันกับ Math.ceil ()


4
ทำไมคำตอบนี้ถึงได้ไกลเมื่อ op ถามหา C # อย่างชัดเจน!
felickz

2
คุณต้องแปลงผลลัพธ์เป็นintเพราะMath.Ceilingส่งกลับค่าdoubleหรือdecimalขึ้นอยู่กับประเภทอินพุต
DanM7

15
เพราะมันไม่มีประสิทธิภาพอย่างยิ่ง
Zar Shardan

6
อาจไม่มีประสิทธิภาพ แต่เข้าใจง่ายมาก ได้รับการคำนวณนับหน้ามักจะทำครั้งเดียวต่อคำขอการสูญเสียประสิทธิภาพใด ๆ จะไม่สามารถวัดได้
Jared Kells

1
สามารถอ่านได้ง่ายกว่านี้ "(เงินปันผล + (ตัวหาร - 1)) / ตัวหาร" ยังช้าและต้องใช้ห้องสมุดคณิตศาสตร์
ม้วน

68

สิ่งนี้จะให้สิ่งที่คุณต้องการ คุณจะต้องการให้ x รายการหารด้วย y รายการต่อหน้าปัญหาคือเมื่อตัวเลขไม่สม่ำเสมอดังนั้นหากมีบางส่วนเราต้องการเพิ่มอีกหนึ่งหน้า

int x = number_of_items;
int y = items_per_page;

// with out library
int pages = x/y + (x % y > 0 ? 1 : 0)

// with library
int pages = (int)Math.Ceiling((double)x / (double)y);

5
x / y + !! (x% y) หลีกเลี่ยงสาขาสำหรับภาษา C-like อัตราต่อรองเป็นสิ่งที่ดี แต่คอมไพเลอร์ของคุณจะทำเช่นนั้น
Rhys Ulerich

2
+1 สำหรับการไม่ล้นเหมือนคำตอบข้างต้น ... แม้ว่าการแปลง ints เป็นสองเท่าสำหรับ Math.ceiling แล้วกลับมาอีกครั้งเป็นความคิดที่ไม่ดีในโค้ดที่ไวต่อประสิทธิภาพ
ล้อเฟือง

3
@RhysUlerich ที่ไม่ทำงานใน c # (ไม่สามารถแปลง int เป็นบูลได้โดยตรง) ทางออกของ rjmunro เป็นวิธีเดียวที่จะหลีกเลี่ยงการแตกกิ่งผมคิดว่า
smead

18

วิธีแก้ปัญหาคณิตศาสตร์จำนวนเต็มที่ Ian จัดไว้นั้นดี แต่ก็มีปัญหาจากข้อผิดพลาดที่ล้นจำนวนเต็ม สมมติว่าตัวแปรทั้งหมดintเป็นโซลูชันสามารถเขียนใหม่เพื่อใช้longคณิตศาสตร์และหลีกเลี่ยงข้อผิดพลาด:

int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;

หากrecordsเป็น a longข้อผิดพลาดจะยังคงอยู่ โซลูชันโมดูลัสไม่มีข้อผิดพลาด


4
ฉันไม่คิดว่าคุณกำลังจะโดนแมลงตัวนี้ในสถานการณ์ที่นำเสนอ 2 ^ 31 บันทึกค่อนข้างมากที่จะต้องผ่านหน้า
rjmunro

8
@rjmunro ตัวอย่างจริงของโลกที่นี่
finnw

@finnw: AFAICS ไม่มีตัวอย่างจริงในหน้านั้นเพียงรายงานจากคนอื่นที่พบข้อบกพร่องในสถานการณ์เชิงทฤษฎี
rjmunro

5
ใช่ฉันเป็นคนคล่องแคล่วในการชี้จุดบกพร่อง ข้อบกพร่องหลายอย่างสามารถมีอยู่ในความเป็นอมตะโดยไม่ทำให้เกิดปัญหาใด ๆ มีข้อผิดพลาดของรูปแบบเดียวกันในการใช้งาน binarySearch ของ JDK เป็นเวลาเก้าปีก่อนที่จะมีคนรายงาน ( googleresearch.blogspot.com/2006/06/… ) ฉันเดาว่าคำถามคือไม่ว่าคุณจะมีข้อบกพร่องนี้ไม่มากแค่ไหนทำไมไม่ลองแก้ไขมันดูล่ะ?
Brandon DuRette

4
นอกจากนี้ควรสังเกตว่าไม่ใช่แค่จำนวนองค์ประกอบที่ได้รับการเพจเท่านั้น แต่ยังมีขนาดหน้ากระดาษด้วย ดังนั้นหากคุณกำลังสร้างห้องสมุดและมีคนเลือกที่จะไม่เพจโดยผ่าน 2 ^ 31-1 (Integer.MAX_VALUE) เป็นขนาดหน้าดังนั้นข้อผิดพลาดจะถูกเรียก
Brandon DuRette

7

ตัวแปรของคำตอบของNick Berardiที่หลีกเลี่ยงสาขา:

int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));

หมายเหตุ: (-r >> (Integer.SIZE - 1))ประกอบด้วยเครื่องหมายบิตของrทำซ้ำ 32 ครั้ง (ขอบคุณเครื่องหมายส่วนขยายของ>>โอเปอเรเตอร์) ค่านี้ประเมินเป็น 0 หากrเป็นศูนย์หรือลบ -1 ถ้าrเป็นค่าบวก ดังนั้นการลบได้จากqมีผลกระทบของการเพิ่ม 1 records % recordsPerPage > 0ถ้า


4

สำหรับบันทึก == 0 โซลูชันของ rjmunro ให้ 1 วิธีแก้ปัญหาที่ถูกต้องคือ 0 นั่นบอกว่าถ้าคุณรู้ว่าบันทึก> 0 (และฉันแน่ใจว่าเราทุกคนสันนิษฐาน recordPerPage> 0) แล้วโซลูชัน rjmunro ให้ผลลัพธ์ที่ถูกต้องและ ไม่มีปัญหาการล้นใด ๆ

int pageCount = 0;
if (records > 0)
{
    pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required

ทั้งหมดการแก้ปัญหาทางคณิตศาสตร์จำนวนเต็มจะมีประสิทธิภาพมากขึ้นกว่าที่ใด ๆของการแก้จุดลอย


วิธีนี้ไม่น่าจะเป็นคอขวดของประสิทธิภาพ และถ้าเป็นเช่นนั้นคุณควรพิจารณาค่าใช้จ่ายของสาขาด้วย
finnw

4

ต้องการวิธีการขยาย:

    public static int DivideUp(this int dividend, int divisor)
    {
        return (dividend + (divisor - 1)) / divisor;
    }

ไม่มีการตรวจสอบที่นี่ (ล้น, DivideByZeroฯลฯ ) อย่าลังเลที่จะเพิ่มถ้าคุณต้องการ โดยวิธีการสำหรับผู้ที่กังวลเกี่ยวกับค่าใช้จ่ายในการภาวนาวิธีการฟังก์ชั่นที่เรียบง่ายเช่นนี้อาจจะมีการคอมไพเลอร์อย่างไรก็ตามฉันไม่คิดว่ามันเป็นที่ที่ต้องกังวล ไชโย

ป.ล. คุณอาจพบว่ามีประโยชน์ที่จะต้องตระหนักถึงสิ่งนี้เช่นกัน (ได้รับส่วนที่เหลือ):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);

1
สิ่งนี้ไม่ถูกต้อง ตัวอย่างเช่น: DivideUp(4, -2)ส่งคืน 0 (ควรเป็น -2) มันถูกต้องเฉพาะสำหรับจำนวนเต็มไม่เป็นลบซึ่งไม่ชัดเจนจากคำตอบหรือจากส่วนต่อประสานของฟังก์ชัน
Thash

6
ทำไมคุณไม่ทำสิ่งที่มีประโยชน์เช่นเพิ่มการตรวจสอบเพิ่มเติมเล็กน้อยถ้าตัวเลขเป็นค่าลบแทนที่จะโหวตคำตอบของฉันลงและสร้างคำสั่งแบบครอบคลุม: "นี่ไม่ถูกต้อง" ในความเป็นจริงมันเป็นเพียงแค่ขอบ กรณี. ฉันได้ระบุไว้อย่างชัดเจนว่าคุณควรทำการตรวจสอบอื่นก่อน: "ไม่มีการตรวจสอบที่นี่ (ล้น, DivideByZero, ฯลฯ ) โปรดเพิ่มหากคุณต้องการ "
Nicholas Petersen

1
คำถามที่กล่าวถึง "ฉันกำลังคิดโดยเฉพาะอย่างยิ่งวิธีการแสดงการควบคุมการให้เลขหน้า " ดังนั้นตัวเลขติดลบจะออกนอกขอบเขตไปแล้ว อีกครั้งเพียงทำสิ่งที่มีประโยชน์และแนะนำการตรวจสอบเพิ่มถ้าคุณต้องการมันเป็นความพยายามของทีม
Nicholas Petersen

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

2

อีกทางเลือกหนึ่งคือการใช้ฟังก์ชั่น mod () (หรือ '%') หากไม่มีส่วนที่เหลือเป็นศูนย์ให้เพิ่มผลลัพธ์จำนวนเต็มของการหาร


1

ฉันทำสิ่งต่อไปนี้จัดการกับโอเวอร์โฟลว์ใด ๆ :

var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;

และใช้ส่วนขยายนี้หากมีผลลัพธ์ 0 รายการ:

public static bool IsDivisble(this int x, int n)
{
           return (x%n) == 0;
}

นอกจากนี้สำหรับหมายเลขหน้าปัจจุบัน (ไม่ได้ถาม แต่อาจมีประโยชน์):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;

0

ทางเลือกในการลบการแยกย่อยในการทดสอบสำหรับศูนย์:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);

ไม่แน่ใจว่าสิ่งนี้จะใช้ได้ใน C # หรือไม่ควรทำใน C / C ++


-1

วิธีการทั่วไปซึ่งผลลัพธ์ที่คุณสามารถทำซ้ำอาจเป็นที่สนใจ:

public static Object[][] chunk(Object[] src, int chunkSize) {

    int overflow = src.length%chunkSize;
    int numChunks = (src.length/chunkSize) + (overflow>0?1:0);
    Object[][] dest = new Object[numChunks][];      
    for (int i=0; i<numChunks; i++) {
        dest[i] = new Object[ (i<numChunks-1 || overflow==0) ? chunkSize : overflow ];
        System.arraycopy(src, i*chunkSize, dest[i], 0, dest[i].length); 
    }
    return dest;
}

ฝรั่งมีวิธีคล้ายกัน ( Lists.partition(List, int)) และแดกดันsize()วิธีการที่เกิดขึ้นList( ณr09) ทนทุกข์ทรมานจากข้อผิดพลาดล้นที่ระบุไว้ในคำตอบของแบรนดอน DuRette ของ
finnw

-2

ฉันมีความต้องการที่คล้ายกันซึ่งฉันต้องการแปลงนาทีเป็นชั่วโมง & นาที สิ่งที่ฉันใช้คือ:

int hrs = 0; int mins = 0;

float tm = totalmins;

if ( tm > 60 ) ( hrs = (int) (tm / 60);

mins = (int) (tm - (hrs * 60));

System.out.println("Total time in Hours & Minutes = " + hrs + ":" + mins);

-2

ต่อไปนี้ควรทำการปัดเศษที่ดีกว่าโซลูชันด้านบน แต่ต้องเสียค่าใช้จ่ายของประสิทธิภาพ (เนื่องจากการคำนวณจุดลอยตัวของ 0.5 * rctDenominator):

uint64_t integerDivide( const uint64_t& rctNumerator, const uint64_t& rctDenominator )
{
  // Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)
  return (rctDenominator == 0) ? 0 : (rctNumerator + (int)(0.5*rctDenominator)) / rctDenominator;
}

-4

คุณจะต้องทำการหารจำนวนจุดลอยตัวจากนั้นใช้ฟังก์ชันเพดานเพื่อปัดเศษค่าเป็นจำนวนเต็มถัดไป

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