[0]zero-index clytie0x/zero-index

5.2 ผลรวม มากสุด น้อยสุด

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

ผลรวมและค่าเฉลี่ย

หาผลรวมทำแบบเดียวกับบทที่ 4 คือมีตัวแปรสะสมที่เริ่มจาก 0 แล้วบวกทีละตัว ต่างกันตรงที่คราวนี้เก็บทุกค่าไว้ในอาเรย์ด้วย จึงย้อนกลับมาตอบคำถามที่ต้องรู้ค่าเฉลี่ยก่อนได้ เช่น มีกี่คนที่ได้คะแนนไม่ต่ำกว่าค่าเฉลี่ย

#include <stdio.h>

int main()
{
    int score[1000];
    int n;
    scanf("%d", &n);
    int total = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &score[i]);
        total += score[i]; // (1)!
    }
    double average = (double)total / n;
    int count = 0;
    for (int i = 0; i < n; i++) { // (2)!
        if (score[i] >= average) {
            count++;
        }
    }
    printf("average = %f\n", average);
    printf("count = %d\n", count);
    return 0;
}
  1. อ่านไปบวกไปในลูปเดียวกันได้ ไม่ต้องแยกลูป
  2. ลูปรอบที่สอง ย้อนกลับไปดูทุกช่องอีกครั้ง หลังจากรู้ค่าเฉลี่ยแล้ว
6
70 85 90 65 80 58
average = 74.666667
count = 3

อย่าลืมเรื่องขนาดจากหน้า 1.3 คะแนนไม่เกิน 100 จำนวนคนไม่เกิน 1000 ผลรวมไม่เกินแสน int พอ แต่ถ้าโจทย์ให้ N ถึง 100000 และแต่ละค่าถึง 109 ผลรวมถึง 1014 ต้องใช้ long long สำหรับตัวแปรสะสม

เลี่ยงทศนิยมได้ก็เลี่ยง

score[i] >= total / n ในทางคณิตศาสตร์เท่ากับ score[i] * n >= total แบบหลังเป็นจำนวนเต็มล้วน ไม่มีการหารที่ตัดเศษทิ้ง และไม่มีทศนิยมคลาดเคลื่อนแบบหน้า 1.4 โจทย์ที่เทียบกับค่าเฉลี่ยมักใช้วิธีนี้ แต่ต้องเช็กว่า score[i] * n ไม่ล้น int

ค่ามากสุด

หน้า 4.2 หาค่ามากสุดตอนอ่านข้อมูลทีละตัวไปแล้ว หลักเดิมใช้ได้ทุกอย่าง คือจำค่าที่มากที่สุดเท่าที่เห็นมา และให้ตัวแรกเป็นค่าเริ่มต้น ต่างกันแค่ตอนนี้ตัวแรกคือ temp[0] และทุกค่ายังอยู่ในอาเรย์ จึงจำ index แทนค่าได้ ซึ่งเป็นเรื่องหลักของหน้านี้ในหัวข้อถัดไป

#include <stdio.h>

int main()
{
    int temp[100];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &temp[i]);
    }
    int max_temp = temp[0];
    for (int i = 1; i < n; i++) {
        if (temp[i] > max_temp) {
            max_temp = temp[i];
        }
    }
    printf("%d\n", max_temp);
    return 0;
}
7
33 35 34 36 32 36 35
36

ข้อมูลเข้าคืออุณหภูมิสูงสุดของ 7 วันในหนึ่งสัปดาห์ บรรทัดที่ 11 เริ่มจากค่าในช่อง 0 ลูปจึงเริ่มที่ i = 1 เพราะไม่ต้องเทียบช่อง 0 กับตัวมันเอง ค่าน้อยสุดก็เขียนแบบเดียวกัน แค่กลับเครื่องหมายเป็น <

ตำแหน่งของค่ามากสุด

โจทย์มักถามว่า "วันไหน" ร้อนที่สุด ไม่ใช่แค่ร้อนเท่าไร วิธีที่ง่ายที่สุดคือ จำ index แทนค่า ถ้ารู้ index ก็หยิบค่าจากอาเรย์ได้เสมอ

สังเกตข้อมูลข้างบน 36 องศามีสองวัน คือวันที่ 4 กับวันที่ 6 โจทย์ต้องบอกว่าถ้าเท่ากันให้ตอบวันไหน และเครื่องหมายเปรียบเทียบตัวเดียวเป็นตัวตัดสิน

#include <stdio.h>

int main()
{
    int temp[100];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &temp[i]);
    }
    int first = 0;
    int last = 0;
    for (int i = 1; i < n; i++) {
        if (temp[i] > temp[first]) { // (1)!
            first = i;
        }
        if (temp[i] >= temp[last]) { // (2)!
            last = i;
        }
    }
    printf("first hottest day = %d\n", first + 1); // (3)!
    printf("last hottest day = %d\n", last + 1);
    printf("temperature = %d\n", temp[first]);
    return 0;
}
  1. > เปลี่ยนเฉพาะเมื่อเจอค่าที่มากกว่าจริงๆ เจอค่าเท่ากันจะไม่เปลี่ยน จึงได้ตำแหน่ง แรก
  2. >= เจอค่าเท่ากันก็เปลี่ยน จึงได้ตำแหน่ง สุดท้าย
  3. first เป็น index ซึ่งเริ่มที่ 0 แต่คนนับวันเริ่มที่ 1 จึงบวกหนึ่งตอนพิมพ์
7
33 35 34 36 32 36 35
first hottest day = 4
last hottest day = 6
temperature = 36

ลองนับเอง 36 องศาตัวแรกอยู่ช่อง 3 ซึ่งคือวันที่ 4 ตัวที่สองอยู่ช่อง 5 ซึ่งคือวันที่ 6 อ่านโจทย์ให้ดีว่าขอตำแหน่งนับจาก 0 หรือจาก 1 และถ้าเท่ากันให้ตอบตัวไหน สองเรื่องนี้ทำคนเสียคะแนนบ่อยกว่าตัววิธีคิดเสียอีก

มากสุดและน้อยสุดในลูปเดียว

หามากสุดกับน้อยสุดในลูปเดียวกันได้ เช่น โจทย์ถามว่าอุณหภูมิในสัปดาห์นี้แกว่งมากที่สุดกี่องศา คำตอบคือค่ามากสุดลบค่าน้อยสุด

#include <stdio.h>

int main()
{
    int temp[100];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &temp[i]);
    }
    int max_temp = temp[0];
    int min_temp = temp[0];
    for (int i = 1; i < n; i++) {
        if (temp[i] > max_temp) {
            max_temp = temp[i];
        }
        if (temp[i] < min_temp) {
            min_temp = temp[i];
        }
    }
    printf("max = %d\n", max_temp);
    printf("min = %d\n", min_temp);
    printf("range = %d\n", max_temp - min_temp);
    return 0;
}
7
33 35 34 36 32 36 35
max = 36
min = 32
range = 4

ทั้งสองค่าเริ่มจาก temp[0] เหมือนกัน แล้วต่างคนต่างเปลี่ยน จึงเขียนเป็น if สองตัวแยกกัน ใช้ else if ก็ได้ผลเท่ากัน เพราะค่าที่มากกว่าค่ามากสุดย่อมไม่น้อยกว่าค่าน้อยสุด แต่แบบแยกอ่านง่ายกว่า และไม่ต้องหยุดคิดว่าเป็นไปได้ไหม

ลองทำให้พัง

แบบที่ 1 เริ่มค่ามากสุดที่ 0

กับดักเดียวกับแบบที่ 2 ในหน้า 4.2 ยังอยู่เมื่อเปลี่ยนมาใช้อาเรย์ ถ้าเริ่ม int max_temp = 0; แล้ววนตั้งแต่ช่อง 0 อุณหภูมิช่องแช่แข็ง -18 -20 -16 -19 -18 จะได้ 0 ซึ่งไม่มีในข้อมูลเลย แทนที่จะได้ -16 แก้ด้วยการเริ่มจาก temp[0] แบบโปรแกรม max-temp ส่วนวิธีจำ index แบบโปรแกรม hottest-day ไม่มีทางพลาดแบบนี้ตั้งแต่แรก เพราะ first = 0 หมายถึงข้อมูลตัวแรกจริงๆ ไม่ใช่ค่าที่เดาขึ้นมาเอง

แบบที่ 2 สับสนระหว่าง index กับค่า

ในโปรแกรม hottest-day ถ้าเปลี่ยนการเปรียบเทียบเป็น if (temp[i] > first) คือเอาค่าไปเทียบกับ index

#include <stdio.h>

int main()
{
    int temp[100];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &temp[i]);
    }
    int first = 0;
    for (int i = 1; i < n; i++) {
        if (temp[i] > first) {
            first = i;
        }
    }
    printf("first hottest day = %d\n", first + 1);
    return 0;
}
7
33 35 34 36 32 36 35
first hottest day = 7

compiler ไม่เตือน เพราะทั้งคู่เป็น int เหมือนกัน แต่ความหมายต่างกันคนละเรื่อง อุณหภูมิทุกวันมากกว่าเลข index เสมอ first จึงเปลี่ยนทุกรอบจนได้วันสุดท้าย ตั้งชื่อตัวแปรที่เก็บ index ให้บอกว่าเป็นตำแหน่ง แล้วเวลาเทียบ ให้เทียบค่ากับค่า คือ temp[i] กับ temp[first]

สรุป

  • ผลรวมใช้ตัวแปรสะสมเริ่มที่ 0 ระวังล้น int ตามขอบเขตในโจทย์
  • เก็บข้อมูลในอาเรย์ทำให้วนรอบสองได้ เช่น นับคนที่ไม่ต่ำกว่าค่าเฉลี่ย
  • ค่ามากสุดและน้อยสุดเริ่มจากช่อง 0 เสมอ อย่าเริ่มจาก 0
  • อยากรู้ตำแหน่ง ให้จำ index แล้วหยิบค่าด้วย a[index]
  • > ได้ตำแหน่งแรก >= ได้ตำแหน่งสุดท้าย index บวกหนึ่งคือลำดับที่คนนับ