אתגר Advent Of Code יום שני

הי,

@YOEL, @11115, @splintor, @Amirkr וכל מי שעוד ירצה להצטרף.

אנחנו ממשיכים באימון לקראת Advent Of Code של השנה ועוברים לתרגיל של היום השני. גם הפעם יש שני חלקים לתרגיל אז בשישי בבוקר אפרסם כאן את החלק השני ובמוצ״ש הפיתרונות וסיכום.

ספרת ביקורת

כשמעבירים מידע מצד לצד בתקשורת מקובל להוסיף ספרת ביקורת שתפקידה לוודא שהמידע לא התקלקל בדרך בטעות. דוגמא שאתם בטח מכירים היא תעודת הזהות בה הספרה הימנית ביותר היא ספרת ביקורת שעוזרת לוודא שלא טעיתם בהקלדת אחת הספרות האחרות. אפשר לקרוא עוד על המנגנון בויקיפדיה כאן:

בתרגיל של היום עלינו לחשב ספרת ביקורת לגיליון נתונים לפי הכללים הבאים: בכל שורה יש לקחת את ההפרש בין הערך הגדול ביותר לערך הקטן ביותר ולסכום את כל ההפרשים. התוצאה היא ספרת הביקורת של הגיליון.

לדוגמא בגיליון הנתונים הבא:

5 1 9 5
7 5 3
2 4 6 8

בשורה הראשונה הערך הגדול ביותר הוא 9 והקטן ביותר הוא 1. ההפרש ביניהם הוא 8.
בשורה השניה הערך הגדול ביותר הוא 7 והקטן ביותר 3. ההפרש ביניהם הוא 4.
בשורה השלישית הערך הגדול ביותר הוא 8 והקטן ביותר 2. ההפרש ביניהם הוא 6.

סכום ההפרשים הוא 18 (8+4+6) ולכן זו ספרת הביקורת של הגיליון.

כתבו קוד שמקבל גיליון (למשל קורא את הנתונים מקובץ) ומחשב את סיפרת הביקורת עליו. אחרי זה לכו לחידה אצל אריק וקחו ממנו את הקלט שלכם כדי לקבל את הכוכב. זה הקישור:
https://adventofcode.com/2017/day/2

לייק 1
function checksum1(s) {
    return s.split(/[\r\n]+/).reduce((sum, c) => sum += Math.max(...c.split(/\W+/)) - Math.min(...c.split(/\W+/)), 0);
}
לייק 1

אני אוהב במצבים כאלה להוסיף map לפני ה reduce- ה map מחשב את ההפרש הדרוש לכל שורה וה reduce יסכום

ועוד אגב אני מעדיף להיות עקבי בשמות הפרמטרים שעוברים לפונקציה ב deduce ותמיד קורא לראשון acc (קיצור של accumulator) ולשני val או value

נ.ב נוסף הפיתרון שלך גם הזכיר לי את הדיבור על זה:

תודה. בדיוק חשבתי על ה-map והתכוונתי לעדכן את הפתרון שלי.

function checksum1(s) {
    return s.split(/[\r\n]+/)
        .map(l => l.split(/\W+/))
        .reduce((acc, c) => acc + Math.max(...c) - Math.min(...c), 0);
}

וכן, אופרטור הצינור יהיה מגניב…
למרות שאני לא רואה איך זה יעזור כאן. הפונקציה שלי נשמעת (לי…) מאוד הגיונית:

קח את הקלט, חלק לשורות, כל שורה חלק למערך של איברים, ואז סכום את ההפרשים בין המינימום למקסימום של כל המערכים.

הי,
הפתרון הבא פותר את התרגיל אבל כשניסיתי את הקלט הארוך של אריק התשובה לא היתה נכונה.
לא הבנתי מדוע

path_r = r"C:\Python\Advent_of_code\exercise_2.txt"
temp = 0


with open(path_r,"r") as f_r:
    a = [line.split() for line in f_r]

    for i in a:
        for j in i:
            temp += int(max(j)) - int(min(j))
      

print temp

מה אם תנסה להדפיס אחרי כל שורה מה ההפרש שמצאת, מה השורה ומה הסכום עד עכשיו? אולי זה יעזור להבין מה הטעות

לייק 1

אני לא רואה איך זה יכול לעזור כי אין לי באמת אינדיקציה לדעת אם בסוף השורה יצא לי נכון או לא

מה זאת אומרת? להסתכל בעין ולראות אם יצא נכון…

לפני שתדפיס את הערכים שאתה מקבל ב-a, i ו-j, תנסה להסביר (לעצמך/לנו) מה כל אחד מהם אמור לייצג.
בהדפסה תוכל לבדוק את עצמך.

פתרון בJava:

חלק א’:

/**  Receives input as String and split it by new-line token. 
For each line, calculate difference of max and min. */
public static int checksum(String input) {
		String[] lines = input.split("\n");
		int sum = 0;
		for(String line : lines)
			sum += greatestDiff(line);
		return sum;
	}
/** Receives a line of values, split it by whitespaces and tabs.
Init max and min as first value, then iterate over values to find min and max. */
public static int greatestDiff(String line) {
		String[] values = line.split("[ \t]+"); //one or more whitespaces/tabs
		int max = Integer.parseInt(values[0]), min = max;
		for(int i=1, n=values.length; i < n; i++) {
			int curr = Integer.parseInt(values[i]);
			if(curr > max) max = curr;
			else if(curr < min) min = curr;
		}
		return max - min;
	}

.

חלק ב’ (שימוש במתודה checksum):

/** Receives a line of values, split by whitespaces and tabs.
Init an array of type int and parse values from String[].
For each value, check if division with other values givse a whioe value. */
public static int evenDivision(String line) {
		String[] values = line.split("[ \t]+");
		int len = values.length;
		int[] nums = new int[len];
		for(int i=0; i < len; i++)
			nums[i] = Integer.parseInt(values[i]);
		for(int i=0; i < len; i++)
			for(int j=0; j < len; j++) {
				if (i==j) continue;
				if (nums[i] % nums[j] == 0) 
					return nums[i] / nums[j];
			}
		throw new RuntimeException("Could not find two values that divide to a whole number");
	}

זה לדוגמא קוד בשפה שנקראת Elixir שפותר את התרגיל. הוא יותר ארוך מה JavaScript שלך אבל אני חושב שעדיין יש בו קסם מסוים, בעיקר בזכות אופרטור הצינור:

IO.stream(:stdio, :line)  
|> Stream.map(&String.trim/1)
|> Stream.map(&String.split/1)
|> Stream.map(fn(arr) -> Enum.map(arr, &String.to_integer/1) end)
|> Stream.map(fn(arr) -> Enum.max(arr) - Enum.min(arr) end)
|> Enum.sum
|> IO.inspect

ספציפית כאן כמו שאמרת כנראה לא יעזור כי הפונקציות map ו reduce ב JavaScript לקחו את הגישה מונחית העצמים ומופעלות עם נקודה (כמו ב Ruby). זה יותר רלוונטי לפונקציות ספריה שמקבלות את ה״אוביקט״ שעובדות עליו בתור הפרמטר הראשון (בדומה לשפות פונקציונאליות). למשל פונקציות כמו JSON.parse

טוב, עשיתי מה שהצעת
עברתי שורה שורה והדפסתי (אפילו חישבתי ידנית …)
הכל עובד כשורה אני לא מבין למה אריק כתב שזה לא נכון…
:roll_eyes:

וידאת גם שהשורות שהתוכנית שלך רואה הן באמת השורות בקלט? לפעמים הבעיה היא בקריאת הגיליון ואז אם אתה מסתכל רק על מה שהתוכנית מדפיסה לא תראה את הבעיה

חיובי , בדקתי
תנסה בבקשה את הקלט הזה
מה זה יוצא אצלך?

1236 741 557 1029 144 101 1968 2159 1399 80 1139 1167 1695 82 90 2236
2134 106 107 1025 584 619 191 496 80 352 351 2267 1983 1973 97 1244
3227 179 691 3177 172 1636 3781 2020 3339 2337 189 3516 1500 176 159 3279
201 688 364 180 586 659 623 577 188 265 403 670 195 720 115 37
1892 1664 2737 2676 849 2514 923 171 311 218 255 2787 1271 188 1278 2834
150 3276 204 603 3130 587 3363 3306 2890 127 176 174 383 3309 213 1620
5903 3686 200 230 6040 4675 6266 179 5375 1069 283 82 6210 6626 6398 1954
942 2324 1901 213 125 2518 655 189 2499 160 2841 2646 198 173 1841 200
232 45 272 280 44 248 50 266 296 297 236 254 58 212 276 48
563 768 124 267 153 622 199 591 204 125 93 656 198 164 797 506
243 4746 1785 204 568 4228 2701 4303 188 4148 4831 1557 4692 166 4210 3656
72 514 1572 172 1197 750 1392 1647 1587 183 1484 213 1614 718 177 622
1117 97 2758 2484 941 1854 1074 264 2494 83 1434 96 2067 2825 2160 92
2610 1290 204 2265 1374 2581 185 852 207 175 3308 1500 2898 1120 1892 3074
2322 1434 301 2156 98 2194 587 1416 1521 94 1985 424 91 119 1869 1073
66 87 176 107 2791 109 21 92 3016 2239 1708 3175 3210 2842 446 484

התוצאה אצלי יצאה 42299

לי יצא רק 1368
אולי תוכל להגיד לי מה הבעיה?

אני לא יודע מה הבעיה - אחרת הייתי שמח להגיד. הוספתי לתוכנית שלי קצת הודעות הדפסה אחרי כל שורה ואז הפלט נראה כך:

1236 741 557 1029 144 101 1968 2159 1399 80 1139 1167 1695 82 90 2236
sum so far: 2156
2134 106 107 1025 584 619 191 496 80 352 351 2267 1983 1973 97 1244
sum so far: 4343
3227 179 691 3177 172 1636 3781 2020 3339 2337 189 3516 1500 176 159 3279
sum so far: 7965
201 688 364 180 586 659 623 577 188 265 403 670 195 720 115 37
sum so far: 8648
1892 1664 2737 2676 849 2514 923 171 311 218 255 2787 1271 188 1278 2834
sum so far: 11311
150 3276 204 603 3130 587 3363 3306 2890 127 176 174 383 3309 213 1620
sum so far: 14547
5903 3686 200 230 6040 4675 6266 179 5375 1069 283 82 6210 6626 6398 1954
sum so far: 21091
942 2324 1901 213 125 2518 655 189 2499 160 2841 2646 198 173 1841 200
sum so far: 23807
232 45 272 280 44 248 50 266 296 297 236 254 58 212 276 48
sum so far: 24060
563 768 124 267 153 622 199 591 204 125 93 656 198 164 797 506
sum so far: 24764
243 4746 1785 204 568 4228 2701 4303 188 4148 4831 1557 4692 166 4210 3656
sum so far: 29429
72 514 1572 172 1197 750 1392 1647 1587 183 1484 213 1614 718 177 622
sum so far: 31004
1117 97 2758 2484 941 1854 1074 264 2494 83 1434 96 2067 2825 2160 92
sum so far: 33746
2610 1290 204 2265 1374 2581 185 852 207 175 3308 1500 2898 1120 1892 3074
sum so far: 36879
2322 1434 301 2156 98 2194 587 1416 1521 94 1985 424 91 119 1869 1073
sum so far: 39110
66 87 176 107 2791 109 21 92 3016 2239 1708 3175 3210 2842 446 484
sum so far: 42299
42299

נסה להוסיף את אותן הדפסות גם לתוכנית שלך ותראה באיזה שורה החישוב מתקלקל

טוב לפי הפלט שלך נראה שיש לי פה פספוס על ההתחלה
סכום הפרשים של השורה הראשונה אצלי הוא 99
לפי החישוב שלי זה גם נכון כי ביקשו את סכום ההפרשים של כל מספר בשורה (כלומר הספרה הגדולה פחות הספרה הקטנה בכל מספר) וזה אכן כך…
איך זה יוצא 2156 ?

זה לא מה שבקשו…

בקשו את סכום ההפרשים של כל שורה. כלומר לכל שורה, למצוא את המספר הגדול ביותר, והקטן ביותר ולחשב את ההפרש ביניהם (כלומר מספר אחד לכל שורה).

הבעיה שלך (שגם אני נפלתי עליה בהתחלה) היא שבקלט לדוגמה, כל מספר הוא בן ספרה אחת, אבל בקלט שמקבלים, בכל מספר יש כמה ספרות…

למשל בשורה הראשונה:

1236 741 557 1029 144 101 1968 2159 1399 80 1139 1167 1695 82 90 2236

המספר הכי קטן הוא 80 והמספר הכי גדול הוא 2236 אז המספר שצריך להוסיף לסכום עבור השורה הזו הוא ההפרש ביניהם: 2156.

2 לייקים

פיתרון JAVA
לחלק הראשון

    public static int checkDifference() throws FileNotFoundException {
        Scanner scan = new Scanner(new File("path"));
        int sum = 0;
        while (scan.hasNextLine()) {
            String line = scan.nextLine();
            String[] arrayLineStr = line.split("\t");
            int[] arrayInt = Arrays.asList(arrayLineStr).stream().mapToInt(Integer::parseInt).toArray();
            int high = 0;
            int low = arrayInt[0];
            for (int i : arrayInt) {
                if (i > high) {high = i;}
                if (i < low) {low = i;}
            }
            sum += (high - low);
        }
        return sum;
    }

וזה לחלק השני

    public static int checkDifference2() throws FileNotFoundException {
        Scanner scan = new Scanner(new File("C:path"));
        int sum = 0;
        while (scan.hasNextLine()) {
            String line = scan.nextLine();
            String[] arrayLineStr = line.split("\t");
            int[] arrayInt = Arrays.asList(arrayLineStr).stream().mapToInt(Integer::parseInt).toArray();
            boolean find = false;
            for (int i : arrayInt) {
                for (int j : arrayInt) {
                    if (i != j) {
                        if (i % j == 0) {
                            sum += (i / j);
                            find = true;
                            break;
                        }
                    }//if i != j
                }
                if (find == true) break;
            }
        }
        return sum;
    }
לייק 1