🌟 Advent of Code (AoC) 2024 🌟

PermalÀnk
Medlem
●

Dag: 6
SprÄk: Java
Lösning: GitHub

Del 1 ~4 ms
Del 2 ~548 ms

PermalÀnk
Medlem ★
●

Dag: 6
SprÄk: Python (numpy)

import numpy as np offset = np.array([[-1, 0],[0, 1],[1, 0],[0, -1]]) d = "0123" original = np.array([[c for c in l.strip()] for l in open("input06.txt", "r").readlines()]) start = tuple(zip(*np.where(original == '^')))[0] def find_loop(a): dir = 0 pos = next = start while (0 <= next[0] < a.shape[0] and 0 <= next[1] < a.shape[1]): if a[next] == '#': dir = (dir + 1) % 4 else: pos = next if a[pos] == d[dir]: return True a[pos] = d[dir] next = tuple(pos + offset[dir]) return False def relabel_visited(a): for i in range(4): a[a == d[i]] = 'X' def part1(a): find_loop(a) # We will not find a loop but it walks the path relabel_visited(a) return a[a == 'X'].size + (a[start] == '^') def test_obstacle(pos): a = original.copy() a[pos] = '#' return find_loop(a) def part2(indices): return sum(test_obstacle(pos) for pos in indices) a = original.copy() print(part1(a)) print(part2(zip(*np.where(a == 'X'))))

Dold text
PermalÀnk
Medlem ★
●
Skrivet av Ingetledigtnamn:

Dag: 6
SprÄk: Python (numpy)

import numpy as np offset = np.array([[-1, 0],[0, 1],[1, 0],[0, -1]]) d = "0123" original = np.array([[c for c in l.strip()] for l in open("input06.txt", "r").readlines()]) start = tuple(zip(*np.where(original == '^')))[0] def find_loop(a): dir = 0 pos = next = start while (0 <= next[0] < a.shape[0] and 0 <= next[1] < a.shape[1]): if a[next] == '#': dir = (dir + 1) % 4 else: pos = next if a[pos] == d[dir]: return True a[pos] = d[dir] next = tuple(pos + offset[dir]) return False def relabel_visited(a): for i in range(4): a[a == d[i]] = 'X' def part1(a): find_loop(a) # We will not find a loop but it walks the path relabel_visited(a) return a[a == 'X'].size + (a[start] == '^') def test_obstacle(pos): a = original.copy() a[pos] = '#' return find_loop(a) def part2(indices): return sum(test_obstacle(pos) for pos in indices) a = original.copy() print(part1(a)) print(part2(zip(*np.where(a == 'X'))))

Dold text

Har du testat att jÀmföra exekveringstid om du implementerar samma lösning med och utan numpy? Min naiva lösning (prova att sÀtta ett block pÄ varje plats i vÄr path och kolla om det loopar) kör pÄ typ 20s pÄ en M1 Pro.

Visa signatur

:(){ :|:& };:

đŸŠđŸ»â€â™‚ïž   đŸšŽđŸ»â€â™‚ïž   đŸƒđŸ»â€â™‚ïž   ☕

PermalÀnk
Medlem
●

Dag 6:
Sprak: Golang

Del 2 gÄr segare Àn jag önskar ~2s

package main import ( "bufio" "fmt" "os" ) type Position struct { y int x int } func isOutOfBounds(rows []string, pos Position) bool { if pos.y < 0 || pos.y >= len(rows) { return true } else if pos.x < 0 || pos.x >= len(rows[pos.y]) { return true } return false } func getNewDirection(dir Position) Position { if dir.y == -1 && dir.x == 0 { // north return Position{0, 1} // east } else if dir.y == 0 && dir.x == 1 { // east return Position{1, 0} // south } else if dir.y == 1 && dir.x == 0 { // south return Position{0, -1} // west } else if dir.y == 0 && dir.x == -1 { // west return Position{-1, 0} // north } panic("no supported direction") } func moveDirection(rows []string, pos, dir Position, visitMap map[int]map[int]int) (Position, Position, bool, int) { pos.y += dir.y pos.x += dir.x if isOutOfBounds(rows, pos) { return pos, dir, false, 0 } else if rows[pos.y][pos.x] == '#' || rows[pos.y][pos.x] == 'O' { pos.y -= dir.y pos.x -= dir.x return pos, getNewDirection(dir), true, 0 } // fmt.Println("Visiting", pos.y, pos.x) visitCount := visit(rows, pos, visitMap) return pos, dir, true, visitCount } func visit(rows []string, pos Position, visitMap map[int]map[int]int) int { if _, exist := visitMap[pos.y]; !exist { visitMap[pos.y] = make(map[int]int, len(rows[pos.y])) } visitMap[pos.y][pos.x]++ return visitMap[pos.y][pos.x] } func findStart(rows []string) (start, direction Position) { for y, row := range rows { for x, char := range row { if char == '^' { // direction up start.y, start.x = y, x direction.y, direction.x = -1, 0 return start, direction } } } panic("no start position") } func moveUntilDone(rows []string, pos, dir Position, visitMap map[int]map[int]int) bool { var moving bool = true posCount := 0 for moving { pos, dir, moving, posCount = moveDirection(rows, pos, dir, visitMap) if posCount > 4 { return true } } return false } func getPartOne(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for _, v := range visitMap { count += len(v) } return count } func getPartTwo(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for y, v := range visitMap { for x := range v { if startPos.y == y && startPos.x == x { continue } rows[y] = rows[y][:x] + "O" + rows[y][x+1:] newVisitMap := make(map[int]map[int]int, len(rows)) if moveUntilDone(rows, startPos, startDirection, newVisitMap) { count++ } rows[y] = rows[y][:x] + "." + rows[y][x+1:] } } return count } func getRows(filename string) []string { file, err := os.Open(filename) if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) var rows []string for scanner.Scan() { row := scanner.Text() rows = append(rows, row) } return rows } func main() { fmt.Println("Part one:", getPartOne(getRows("../input.txt"))) fmt.Println("Part two:", getPartTwo(getRows("../input.txt"))) }

Dold text
PermalÀnk
Medlem ★
●
Skrivet av GLaDER:

Har du testat att jÀmföra exekveringstid om du implementerar samma lösning med och utan numpy? Min naiva lösning (prova att sÀtta ett block pÄ varje plats i vÄr path och kolla om det loopar) kör pÄ typ 20s pÄ en M1 Pro.

Tajmade just. Tar ca 40s pÄ min 12900K.

Detta Àr dock inte en uppgift dÀr numpy glÀnser, manipulering av enskilda element Àr inte dess starkaste sida. Vid varje access mÄste numpy testa vad du indexerar med, Àr det heltal, en slice, ett villkor, konstiga listor eller en tupel? Det kostar i prestanda nÀr man gör lite per access. NÀr det blir operationer pÄ stora arrayer dÀremot...

Men att kunna adressera arrayen med tupler och villkor Àr en programmeringsmodell att jag inte kan motstÄ

PermalÀnk
Datavetare ★
●

Dag: 6
SprÄk: Rust

use rayon::prelude::*; use std::fs; pub const INPUT_FILE: &str = "input.txt"; const DIRECTIONS: &[&Vec2D; 4] = &[ &Vec2D { x: 0, y: -1 }, &Vec2D { x: 1, y: 0 }, &Vec2D { x: 0, y: 1 }, &Vec2D { x: -1, y: 0 }, ]; #[derive(Default, Clone, Copy)] pub struct Vec2D { x: i32, y: i32, } pub type Map = Vec<Vec<bool>>; pub fn read_map_and_start_pos(path: &str) -> (Map, Vec2D) { let mut start_pos = Vec2D::default(); let mut map = Map::default(); for (y, line) in fs::read_to_string(path) .expect(&format!("File {} should be present", path)) .lines() .enumerate() { let mut row = Vec::new(); for (x, ch) in line.chars().enumerate() { if '^' == ch { start_pos = Vec2D { x: x as i32, y: y as i32, } } row.push('#' == ch); } map.push(row); } (map, start_pos) } fn calc_num_distinct_tiles(map: &Map, start_pos: &Vec2D, added_block_pos: &Vec2D) -> (u32, bool) { let width = map[0].len() as i32; let height = map.len() as i32; let mut direction = 0; let mut visited = vec![vec![0u8; width as usize]; height as usize]; let mut guard_pos = *start_pos; let mut next_forward_pos = *start_pos; let mut num_distinct_tiles = 0; while next_forward_pos.x >= 0 && next_forward_pos.x < width && next_forward_pos.y >= 0 && next_forward_pos.y < height { if (added_block_pos.x == next_forward_pos.x && added_block_pos.y == next_forward_pos.y) || map[next_forward_pos.y as usize][next_forward_pos.x as usize] { direction = (direction + 1) % 4; } else { guard_pos = next_forward_pos; let visited_tile = &mut visited[guard_pos.y as usize][guard_pos.x as usize]; if *visited_tile == 0 { num_distinct_tiles += 1; } else if (*visited_tile & (1 << direction)) != 0 { return (num_distinct_tiles, true); } *visited_tile |= 1 << direction; } let forward = DIRECTIONS[direction]; next_forward_pos = Vec2D { x: guard_pos.x + forward.x, y: guard_pos.y + forward.y, }; } (num_distinct_tiles, false) } pub fn calc_num_distinct_tiles_before_leaving(map: &Map, start_pos: &Vec2D) -> u32 { let dummy_pos = Vec2D { x: -1, y: -1 }; let (cnt, _) = calc_num_distinct_tiles(map, start_pos, &dummy_pos); cnt } pub fn calc_num_block_position_causing_loop(map: &Map, start_pos: &Vec2D) -> u32 { (0..map.len()) .into_par_iter() .map(|y| { (0..map[0].len()) .filter_map(|x| { let block_pos = Vec2D { x: x as i32, y: y as i32, }; if block_pos.x == start_pos.x && block_pos.y == start_pos.y || map[block_pos.y as usize][block_pos.x as usize] { return None; } let (_, is_loop) = calc_num_distinct_tiles(map, start_pos, &block_pos); if is_loop { Some(1) } else { None } }) .sum::<u32>() }) .sum() } fn main() { let (map, start_pos) = read_map_and_start_pos(INPUT_FILE); println!( "part 1: {}", calc_num_distinct_tiles_before_leaving(&map, &start_pos) ); println!( "part 2: {}", calc_num_block_position_causing_loop(&map, &start_pos) ); }

Dold text

Del 1: 16”s (M3) / 51”s (Orange Pi 5)
Del 2: 20ms (M3) / 147ms (Orange Pi 5)

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Medlem ★
●

Dag: 6
SprÄk: F#

Inte direkt snabb, del tvÄ tar sÀkert 6 sekunder pÄ en M2.

type Direction = UP | DOWN | LEFT | RIGHT type Spot = EMPTY | OBSTACLE | START type Position = {X: int; Y: int} type Movement = { position: Position; direction: Direction } let data = System.IO.File.ReadAllLines("input.txt") |> Seq.mapi (fun x -> Seq.mapi (fun y c -> let spot = match c with | '.' -> EMPTY | '#' -> OBSTACLE | '^' -> START | _ -> failwith "Invalid input" ({X= x; Y=y}, spot) )) |> Seq.concat let startPosition = data |> Seq.find (fun (_, s) -> s = START) |> fst let obstacles = data |> Seq.filter (fun (_, s) -> s = OBSTACLE) |> Seq.map fst |> Set.ofSeq let bounds = let maxX = data |> Seq.map fst |> Seq.maxBy _.X |> _.X let maxY = data |> Seq.map fst |> Seq.maxBy _.Y |> _.Y (maxX, maxY) let move obstacles movement = let potentialNewMove = match movement.direction with | UP -> { movement with position.X = movement.position.X - 1 } | DOWN -> { movement with position.X = movement.position.X + 1 } | LEFT -> { movement with position.Y = movement.position.Y - 1 } | RIGHT -> { movement with position.Y = movement.position.Y + 1 } match Set.contains potentialNewMove.position obstacles with | false -> potentialNewMove | true -> match movement.direction with | UP -> { movement with direction = RIGHT; } | DOWN -> { movement with direction = LEFT;} | LEFT -> { movement with direction = UP;} | RIGHT -> { movement with direction = DOWN;} let MoveUntilOutOfBounds obstacles movement = let rec MoveUntilOutOfBounds' pos cnt = let newMovement = move obstacles pos match newMovement.position.X < 0 || newMovement.position.Y < 0 || newMovement.position.X > fst bounds || newMovement.position.Y > snd bounds with | true -> cnt | false -> MoveUntilOutOfBounds' newMovement (cnt |> Set.add newMovement.position) MoveUntilOutOfBounds' movement Set.empty let visitedPositions = MoveUntilOutOfBounds obstacles { position = startPosition; direction = UP } printfn $"Task 1: %A{visitedPositions |> Set.count}" let createsLoop movement obstacles = let rec MoveUntilOutOfBounds' pos cnt = let newMovement = move obstacles pos if newMovement.position.X < 0 || newMovement.position.Y < 0 || newMovement.position.X > fst bounds || newMovement.position.Y > snd bounds then false elif Set.contains newMovement cnt && newMovement <> pos then true else MoveUntilOutOfBounds' newMovement (cnt |> Set.add newMovement) MoveUntilOutOfBounds' movement Set.empty let obstaclesThatCreatesLoop = visitedPositions |> Set.filter (fun position -> createsLoop { position=startPosition; direction = UP } (Set.add position obstacles)) printfn $"Task 2: %A{obstaclesThatCreatesLoop |> Set.count}"

Dold text
Visa signatur

Jag Àr en optimist; det Àr aldrig sÄ dÄligt sÄ att det inte kan bli sÀmre.

PermalÀnk
Datavetare ★
●
Skrivet av Flexbert:

Dag 6:
Sprak: Golang

Del 2 gÄr segare Àn jag önskar ~2s

package main import ( "bufio" "fmt" "os" ) type Position struct { y int x int } func isOutOfBounds(rows []string, pos Position) bool { if pos.y < 0 || pos.y >= len(rows) { return true } else if pos.x < 0 || pos.x >= len(rows[pos.y]) { return true } return false } func getNewDirection(dir Position) Position { if dir.y == -1 && dir.x == 0 { // north return Position{0, 1} // east } else if dir.y == 0 && dir.x == 1 { // east return Position{1, 0} // south } else if dir.y == 1 && dir.x == 0 { // south return Position{0, -1} // west } else if dir.y == 0 && dir.x == -1 { // west return Position{-1, 0} // north } panic("no supported direction") } func moveDirection(rows []string, pos, dir Position, visitMap map[int]map[int]int) (Position, Position, bool, int) { pos.y += dir.y pos.x += dir.x if isOutOfBounds(rows, pos) { return pos, dir, false, 0 } else if rows[pos.y][pos.x] == '#' || rows[pos.y][pos.x] == 'O' { pos.y -= dir.y pos.x -= dir.x return pos, getNewDirection(dir), true, 0 } // fmt.Println("Visiting", pos.y, pos.x) visitCount := visit(rows, pos, visitMap) return pos, dir, true, visitCount } func visit(rows []string, pos Position, visitMap map[int]map[int]int) int { if _, exist := visitMap[pos.y]; !exist { visitMap[pos.y] = make(map[int]int, len(rows[pos.y])) } visitMap[pos.y][pos.x]++ return visitMap[pos.y][pos.x] } func findStart(rows []string) (start, direction Position) { for y, row := range rows { for x, char := range row { if char == '^' { // direction up start.y, start.x = y, x direction.y, direction.x = -1, 0 return start, direction } } } panic("no start position") } func moveUntilDone(rows []string, pos, dir Position, visitMap map[int]map[int]int) bool { var moving bool = true posCount := 0 for moving { pos, dir, moving, posCount = moveDirection(rows, pos, dir, visitMap) if posCount > 4 { return true } } return false } func getPartOne(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for _, v := range visitMap { count += len(v) } return count } func getPartTwo(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for y, v := range visitMap { for x := range v { if startPos.y == y && startPos.x == x { continue } rows[y] = rows[y][:x] + "O" + rows[y][x+1:] newVisitMap := make(map[int]map[int]int, len(rows)) if moveUntilDone(rows, startPos, startDirection, newVisitMap) { count++ } rows[y] = rows[y][:x] + "." + rows[y][x+1:] } } return count } func getRows(filename string) []string { file, err := os.Open(filename) if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) var rows []string for scanner.Scan() { row := scanner.Text() rows = append(rows, row) } return rows } func main() { fmt.Println("Part one:", getPartOne(getRows("../input.txt"))) fmt.Println("Part two:", getPartTwo(getRows("../input.txt"))) }

Dold text

Liten justering av din lösning, Àr dÄ nere pÄ 0,6s pÄ M3
Go stora styrka Àr inte parallellism, det Àr concurrency och syns hÀr...

Dag: 6
SprÄk: Go

package main import ( "bufio" "fmt" "os" ) type Position struct { y int x int } func isOutOfBounds(rows []string, pos Position) bool { if pos.y < 0 || pos.y >= len(rows) { return true } else if pos.x < 0 || pos.x >= len(rows[pos.y]) { return true } return false } func getNewDirection(dir Position) Position { if dir.y == -1 && dir.x == 0 { // north return Position{0, 1} // east } else if dir.y == 0 && dir.x == 1 { // east return Position{1, 0} // south } else if dir.y == 1 && dir.x == 0 { // south return Position{0, -1} // west } else if dir.y == 0 && dir.x == -1 { // west return Position{-1, 0} // north } panic("no supported direction") } func moveDirection(rows []string, pos, dir Position, visitMap map[int]map[int]int) (Position, Position, bool, int) { pos.y += dir.y pos.x += dir.x if isOutOfBounds(rows, pos) { return pos, dir, false, 0 } else if rows[pos.y][pos.x] == '#' || rows[pos.y][pos.x] == 'O' { pos.y -= dir.y pos.x -= dir.x return pos, getNewDirection(dir), true, 0 } // fmt.Println("Visiting", pos.y, pos.x) visitCount := visit(rows, pos, visitMap) return pos, dir, true, visitCount } func visit(rows []string, pos Position, visitMap map[int]map[int]int) int { if _, exist := visitMap[pos.y]; !exist { visitMap[pos.y] = make(map[int]int, len(rows[pos.y])) } visitMap[pos.y][pos.x]++ return visitMap[pos.y][pos.x] } func findStart(rows []string) (start, direction Position) { for y, row := range rows { for x, char := range row { if char == '^' { // direction up start.y, start.x = y, x direction.y, direction.x = -1, 0 return start, direction } } } panic("no start position") } func moveUntilDone(rows []string, pos, dir Position, visitMap map[int]map[int]int) bool { var moving bool = true posCount := 0 for moving { pos, dir, moving, posCount = moveDirection(rows, pos, dir, visitMap) if posCount > 4 { return true } } return false } func getPartOne(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for _, v := range visitMap { count += len(v) } return count } func deepCopySlice(original []string) []string { copy := make([]string, len(original)) for i, str := range original { copy[i] = str } return copy } func getPartTwo(_rows []string) (count int) { startPos, startDirection := findStart(_rows) visitMap := make(map[int]map[int]int, len(_rows)) visit(_rows, startPos, visitMap) moveUntilDone(_rows, startPos, startDirection, visitMap) cntChan := make(chan int) // CHANGE MADE HERE! for _y, _v := range visitMap { go func(y int, v map[int]int, rows []string) { rowCnt := 0 for x := range v { if startPos.y == y && startPos.x == x { continue } rows[y] = rows[y][:x] + "O" + rows[y][x+1:] newVisitMap := make(map[int]map[int]int, len(rows)) if moveUntilDone(rows, startPos, startDirection, newVisitMap) { rowCnt++ } rows[y] = rows[y][:x] + "." + rows[y][x+1:] } cntChan <- rowCnt }(_y, _v, deepCopySlice(_rows)) } for i := len(visitMap); i > 0; i-- { count += <-cntChan } return count } func getRows(filename string) []string { file, err := os.Open(filename) if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) var rows []string for scanner.Scan() { row := scanner.Text() rows = append(rows, row) } return rows } func main() { fmt.Println("Part one:", getPartOne(getRows("input.txt"))) fmt.Println("Part two:", getPartTwo(getRows("input.txt"))) }

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Medlem ★
●

Dag 7 Del 1 skriker vid första anblick "Permutated Bruteforce" men det kan helt omöjligt vara vad skaparen bakom pusslet vill att man ska nyttja? SÄdana lösningar Àr ju frÀmst till för den tveksamma aktiviteten lösenordshackning.

Jag lider konstant av medveten inkompetens och den paradox det innebÀr. Jag vet att det Àr nÄgot jag inte vet Ànnu men jag vet inte vad det kan vara för jag vet inte ens vad som finns Ànnu!

Är det nĂ„got inom Numbers Theory, nĂ„got inom Graph Theory, nĂ„got inom nĂ„gon annan Matematisk Theory-disciplin vars teori(er) lett till numera kĂ€nda algoritmer sĂ€rskilt framtagna för att lösa just sĂ„dana hĂ€r slags problem mer effektivt Ă€n klassisk "Permutated Bruteforce"?

Om nÄgon vill ge en ledtrÄd för Dag 7 Del 1 till vad för "kÀnda" algoritm(er) inom vilken Matematisk Theory-disciplin jag borde ta en titt pÄ sÄ dela gÀrna det i en spoiler-tagg. Jag har ju redan lÀrt mig nyttja koordinatsystem nu tack vare tidigare dagar och snart lÀr jag inom tid fÄ lÀra mig nyttja Graph Theory med noder och kanter i praktiken.

Tills dess sÄ provar jag "Permutated Bruteforce" (vilket lÀr ej funka vid runtime pÄ grund av för hög !n dÀrmed minnesbrist i PHP)!

EDIT: Jag upptÀckte redan innan jag försökte mig pÄ "Permutated Bruteforce" att det högsta !n jag kommer att fÄ Àr 11 vilket dÄ blir 39916800 vilket innebÀr följande nÀr en sÄdan stor array försöks fyllas:

$test = array_fill(0, 39916800, "999"); var_dump($test); [Running] php "c:\xampp\htdocs\skoj\7.php" PHP Fatal error: Allowed memory size of 943718400 bytes exhausted (tried to allocate 1073741832 bytes) in C:\xampp\htdocs\skoj\7.php on line 865 Fatal error: Allowed memory size of 943718400 bytes exhausted (tried to allocate 1073741832 bytes) in C:\xampp\htdocs\skoj\7.php on line 865

Eller ja, det fungerar om jag har: ini_set('memory_limit', '-1'); men dÄ arbetar VSCode ihjÀl sig och försöker ÀndÄ allokera flera GB trots att det egentligen bara Àr ~1074 MB som behövs för den största möjliga arrayen.

Mvh,
WKF.

Visa signatur

(V)ulnerabilities
(I)n
(B)asically
(E)verything
Programming

PermalÀnk
Medlem
●

Möjliga kombinationer för dag 7 Àr vÀl 3^(n-1) för varje rad. Det blir sÄklart relativt snabbt otrevligt, men borde inte vara nÄgra problem för den hÀr uppgiften.

PermalÀnk
Medlem ★
●
Skrivet av WebbkodsFrilansaren:

Dag 7 Del 1 skriker vid första anblick "Permutated Bruteforce" men det kan helt omöjligt vara vad skaparen bakom pusslet vill att man ska nyttja? SÄdana lösningar Àr ju frÀmst till för den tveksamma aktiviteten lösenordshackning.

Jag lider konstant av medveten inkompetens och den paradox det innebÀr. Jag vet att det Àr nÄgot jag inte vet Ànnu men jag vet inte vad det kan vara för jag vet inte ens vad som finns Ànnu!

Är det nĂ„got inom Numbers Theory, nĂ„got inom Graph Theory, nĂ„got inom nĂ„gon annan Matematisk Theory-disciplin vars teori(er) lett till numera kĂ€nda algoritmer sĂ€rskilt framtagna för att lösa just sĂ„dana hĂ€r slags problem mer effektivt Ă€n klassisk "Permutated Bruteforce"?

Om nÄgon vill ge en ledtrÄd för Dag 7 Del 1 till vad för "kÀnda" algoritm(er) inom vilken Matematisk Theory-disciplin jag borde ta en titt pÄ sÄ dela gÀrna det i en spoiler-tagg. Jag har ju redan lÀrt mig nyttja koordinatsystem nu tack vare tidigare dagar och snart lÀr jag inom tid fÄ lÀra mig nyttja Graph Theory med noder och kanter i praktiken.

Tills dess sÄ provar jag "Permutated Bruteforce" (vilket lÀr ej funka vid runtime pÄ grund av för hög !n dÀrmed minnesbrist i PHP)!

EDIT: Jag upptÀckte redan innan jag försökte mig pÄ "Permutated Bruteforce" att det högsta !n jag kommer att fÄ Àr 11 vilket dÄ blir 39916800 vilket innebÀr följande nÀr en sÄdan stor array försöks fyllas:

$test = array_fill(0, 39916800, "999"); var_dump($test); [Running] php "c:\xampp\htdocs\skoj\7.php" PHP Fatal error: Allowed memory size of 943718400 bytes exhausted (tried to allocate 1073741832 bytes) in C:\xampp\htdocs\skoj\7.php on line 865 Fatal error: Allowed memory size of 943718400 bytes exhausted (tried to allocate 1073741832 bytes) in C:\xampp\htdocs\skoj\7.php on line 865

Eller ja, det fungerar om jag har: ini_set('memory_limit', '-1'); men dÄ arbetar VSCode ihjÀl sig och försöker ÀndÄ allokera flera GB trots att det egentligen bara Àr ~1074 MB som behövs för den största möjliga arrayen.

Mvh,
WKF.

Skriver frÄn mobil sÄ ursÀkta formatering...

Min lösning med ca 60ms Del 1 och ~ 4000ms Del 2(string concatenation Àr inte billigt ...) handlar helt enkelt om att:

För varje rad, generera alla möjliga kombinationer av dina operatorer. Varje rad har "AntalSiffror - 1" operatorer.

Sedan itererar du varje kombination av dessa.
Iterera sedan över varje operator i varje kombination och hantera dina siffror likt följande (möjlig lösning nedan sÄ lÀs bara om du vill)

var before = numbersForLine[operatorIndex] var after = numbersForLine[operatorIndex + 1] if operatorIndex == 1 { if operator==add { value = before + after } else if operator == mult { value = before * after} } else { if operator==add { value += after } else if operator == mult { value *= after} }

Dold text
Dold text
Visa signatur

NZXT H510 Flow MSI B450 Tomahawk MAX
AMD Ryzen 5800X3D RX 7900XTX Kingston Fury 64GB
LG C2 42" 4K@120Hz AOC Q27G2U 1440P@144Hz

PermalÀnk
Medlem
●

Dag 7
SprÄk: Java
GitHub

Konkatenerade genom att konvertera till en strÀng och sen tillbaka först, tiden minskade till ~en tredjedel nÀr jag gjorde den matematiskt istÀllet. Inget jag tÀnkt pÄ tidigare

Dold text
Visa signatur

Starka Äsikter om onödiga saker.

PermalÀnk
Medlem ★
●

Dag: ?
SprÄk: Python

from operator import add, mul def test_operators(expect, terms, operators, computed = 0): if not terms: return expect if expect == computed else 0 for o in operators: if test_operators(expect, terms[1:], operators, o(computed, terms[0])): return expect return 0 s1 = s2 = 0 for l in open("input07.txt", "r").readlines(): expect, terms = l.split(": ") s1 += test_operators(int(expect), list(map(int, terms.split(" "))), [add, mul]) s2 += test_operators(int(expect), list(map(int, terms.split(" "))), [add, mul, lambda x,y: int(str(x) + str(y))]) print(s1, s2)

Dold text

Eller i lite mer one-liner-ish style. Man kommer inte ifrÄn att den rekursiva funktionen mÄste ha ett namn.

from operator import add, mul def test_operators(expect, terms, operators, computed = 0): if not terms: return expect if expect == computed else 0 return any(test_operators(expect, terms[1:], operators, o(computed, terms[0])) for o in operators) and expect print(*[sum(test_operators(expect, terms, os) for expect, terms in [(int(expect), list(map(int, terms.split()))) for expect, terms in (line.split(": ") for line in open("input07.txt", "r").readlines())]) for os in [[add, mul], [add, mul, lambda x,y: int(str(x) + str(y))]]])

Dold text
Lade till en extra lösning.
PermalÀnk
Medlem ★
●

Dag: 7
SprÄk: C#
Dag 7

Del 1: ~27ms
Del 2: ~3700ms

Kan lÀgga in parallellism och bÀttre generering av antalet kombinationer, men nu Àr PoE2 ute sÄ jag nöjer mig med detta

namespace Day_7; public class Solver { private enum Operator { Add, Mult, Concat } private static List<Operator[]> GetOperatorCombinations(int count, List<Operator> operators) { var result = new List<Operator[]>(); GenerateCombinations(operators.ToArray(), count, new Operator[count], result, 0); return result; } private static void GenerateCombinations(Operator[] operators, int count, Operator[] current, List<Operator[]> result, int index) { if (index == count) { result.Add((Operator[])current.Clone()); return; } foreach (var op in operators) { current[index] = op; GenerateCombinations(operators, count, current, result, index + 1); } } private static List<(long Value, List<long> Numbers)> ParseInput(List<string> data) { List<(long val, List<long> numbers)> result = new(); foreach (var line in data) { var value = Int64.Parse(line.Substring(0, line.IndexOf(':'))); var numbers = line[(line.IndexOf(':') + 1)..].Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(s => Int64.Parse(s.Trim())).ToList(); result.Add((val: value, numbers: numbers)); } return result; } public static long Run_PartOne(List<string> input) { var data = ParseInput(input); long totalValue = 0; foreach (var line in data) { long expectedValue = line.Value; var numbers = line.Numbers; var numberOfOperators = numbers.Count - 1; var combinations = GetOperatorCombinations(numberOfOperators, [Operator.Add, Operator.Mult]); for (int i = 0; i < combinations.Count; i++) { long val = 0; var operators = combinations.ElementAt(i); for (int j = 0; j < operators.Length; j++) { var numberBefore = numbers[j]; var numberAfter = numbers[j + 1]; var currentOp = operators[j]; if (j == 0) { if (currentOp == Operator.Add) val = numberBefore + numberAfter; else if (currentOp == Operator.Mult) val = numberBefore * numberAfter; } else { if (currentOp == Operator.Add) val += numberAfter; else if (currentOp == Operator.Mult) val *= numberAfter; } } if (val == expectedValue) { totalValue += val; break; } } } return totalValue; } public static long Run_PartTwo(List<string> input) { var data = ParseInput(input); long totalValue = 0; foreach (var line in data) { long expectedValue = line.Value; var numbers = line.Numbers; var numberOfOperators = numbers.Count - 1; var combinations = GetOperatorCombinations(numberOfOperators, [Operator.Add, Operator.Mult, Operator.Concat]); for (int i = 0; i < combinations.Count; i++) { long val = 0; var operators = combinations.ElementAt(i); for (int j = 0; j < operators.Length; j++) { var numberBefore = numbers[j]; var numberAfter = numbers[j + 1]; var currentOp = operators[j]; if (j == 0) { if (currentOp == Operator.Add) val = numberBefore + numberAfter; else if (currentOp == Operator.Mult) val = numberBefore * numberAfter; else if (currentOp == Operator.Concat) val = long.Parse($"{numberBefore}{numberAfter}"); } else { if (currentOp == Operator.Add) val += numberAfter; else if (currentOp == Operator.Mult) val *= numberAfter; else if (currentOp == Operator.Concat) val = long.Parse($"{val}{numberAfter}"); } } if (val == expectedValue) { totalValue += val; break; } } } return totalValue; } }

Dold text
Visa signatur

NZXT H510 Flow MSI B450 Tomahawk MAX
AMD Ryzen 5800X3D RX 7900XTX Kingston Fury 64GB
LG C2 42" 4K@120Hz AOC Q27G2U 1440P@144Hz

PermalÀnk
Medlem ★
●
Skrivet av Pamudas:

Skriver frÄn mobil sÄ ursÀkta formatering...

Min lösning med ca 60ms Del 1 och ~ 4000ms Del 2(string concatenation Àr inte billigt ...) handlar helt enkelt om att:

För varje rad, generera alla möjliga kombinationer av dina operatorer. Varje rad har "AntalSiffror - 1" operatorer.

Sedan itererar du varje kombination av dessa.
Iterera sedan över varje operator i varje kombination och hantera dina siffror likt följande (möjlig lösning nedan sÄ lÀs bara om du vill)

var before = numbersForLine[operatorIndex] var after = numbersForLine[operatorIndex + 1] if operatorIndex == 1 { if operator==add { value = before + after } else if operator == mult { value = before * after} } else { if operator==add { value += after } else if operator == mult { value *= after} }

Dold text
Dold text

Dag 7 Del 1 Diskussion

SÄ det Àr bruteforce av permutationer av operatorerna + och * trots allt? Det var det första jag tÀnkte pÄ sÄ fort jag gjorde min första tolkning av pusslet idag. Jag förstÄr inte varför PHP verkar vara sÄ minnesuselt sÄ fort en array vill lagra cirka 4 miljoner element? (antalet kombinationer av + och * i den rad med flest nummer, dvs., 12 nummer, dÀrmed 12-1 operatorer och dÀrmed !11 antal kombinationer).

Problemet jag verkar ha Àr inte lösningen i sig utan sjÀlva minneshanteringen (hÀr kommer verkligheten i kapp en nÀr man sluppit minneshantering i snart tre Ärs tid som Webbutvecklare) för att kunna köra lösningen.

Även i förrförra gĂ„rdagens Del 2 fick jag problemet med att jag skulle köra en foreach av reglerna pĂ„ varje kombination av numren, dvs., en foreach i en foreach och funktionen som genererade nummerkombinationer körde rekursivt och fick minnesbrist av samma skĂ€l: för mĂ„nga element i en array.

Btw, tror jag VSCode-minnet pÄ flera GB berodde mest pÄ att jag försökte var_dumpa (skriva ut) i terminalen ~4 miljoner element snarare Àn den faktiska minneslagringen pÄ ~1024 MB för hela arrayen.

Dold text

Mvh,
WKF.

Visa signatur

(V)ulnerabilities
(I)n
(B)asically
(E)verything
Programming

PermalÀnk
Medlem ★
●

Dag 7, C#

Ser att integers blivit för smĂ„, det bĂ„dar inte sĂ„ gott för framtiden. Åtminstone trevligt nĂ€r jag lyckades hjĂ€lpa mig sjĂ€lv pĂ„ lösningen av del 1 sĂ„ pass att del 2 blev trivial.

Nu i efterhand gick jag in för att optimera det, nu kör det pÄ 5 sekunder, var vÀl runt 2 minuter nÀr jag skickade in svaret...

using System.Collections.Concurrent; using System.Diagnostics; var lines = File.ReadAllLines("input.txt").Select(x => x.Split(':')); (long sumpart1, long sumpart2) = (0, 0); long a(long x, long y) => x + y; long m(long x, long y) => x * y; long c(long x, long y) => long.Parse($"{x}{y}"); Stopwatch sw = Stopwatch.StartNew(); ConcurrentBag<long> p1 = []; ConcurrentBag<long> p2 = []; Parallel.ForEach(lines, line => { var expected = long.Parse(line[0]); var numbers = line[1].Trim().Split(' ').Select(long.Parse).Reverse().ToArray(); if (Generatesolutions(numbers, [a, m]).Any(x => x == expected)) p1.Add(expected); else if (Generatesolutions(numbers, [a, m, c]).Any(x => x == expected)) p2.Add(expected); }); sumpart1 = p1.Sum(); sumpart2 = sumpart1 + p2.Sum(); Console.WriteLine("Part 1: " + sumpart1); Console.WriteLine("Part 2: " + sumpart2); Console.WriteLine("Time: " + sw.Elapsed); static IEnumerable<long> Generatesolutions(long[] numbers, IEnumerable<Func<long, long, long>> funcs) { if (numbers.Length == 1) { yield return numbers[0]; yield break; } foreach (var op in funcs) foreach (var num in Generatesolutions(numbers[1..], funcs)) yield return op(num, numbers[0]); }

Dold text
optimerat
Visa signatur

The power of GNU compiles you!
"Often statistics are used as a drunken man uses lampposts -- for support rather than illumination."

PermalÀnk
Medlem
●

Dag: 7
SprÄk: Java
Lösning: GitHub

Del 1: ~ 56 ms
Del 2: ~ 2129 ms
Försökte att fÄ ner tiden pÄ del 2 - det blev vÀrre och vÀrre, slutade med att jag nöjde mig med runt 2-ish sekunder.

PermalÀnk
Medlem
●

Dag: 7
SprÄk: Scala

SjĂ€lva berĂ€kningarna för del1/2 ligger pĂ„ 35/80ÎŒs. Parse funktionen tar mer Ă€n dubbelt sĂ„ lĂ„ng tid.

object Day07: val input = Using.resource(Source.fromFile("7.txt"))(_.getLines().toArray) def parse(input: Array[String]): Array[(Long, List[Int])] = input.map: line => val Array(fst, snd) = line.split(':') fst.toLong -> snd.trim.split(" ").map(_.toInt).reverse.toList def part1(data: Array[(Long, List[Int])]): Long = def canSum(target: Long, values: List[Int]): Boolean = values.nonEmpty && (values.head == target || target % values.head == 0 && canSum(target / values.head, values.tail) || target >= values.head && canSum(target - values.head, values.tail)) data .collect: case (sum, values) if canSum(sum, values) => sum .sum private val table1: Array[Int] = Array(0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3) private val table2: Array[Int] = Array(1, 10, 100, 1000) def part2(data: Array[(Long, List[Int])]): Long = def multiplier(x: Int): Int = var y = table1(31 - Integer.numberOfLeadingZeros(x)) y = y - ((x - table2(y)) >>> 31) table2(y + 1) def canSum(target: Long, values: List[Int]): Boolean = values.nonEmpty && (values.head == target || target % values.head == 0 && canSum(target / values.head, values.tail) || target >= values.head && canSum(target - values.head, values.tail) || { val m = multiplier(values.head) (target - values.head) % m == 0 && canSum(target / m, values.tail) }) data .collect: case (sum, values) if canSum(sum, values) => sum .sum def main(args: Array[String]): Unit = println(part1(parse(input))) println(part2(parse(input)))

Dold text
PermalÀnk
Medlem
●
Skrivet av Yoshman:

Liten justering av din lösning, Àr dÄ nere pÄ 0,6s pÄ M3
Go stora styrka Àr inte parallellism, det Àr concurrency och syns hÀr...

Dag: 6
SprÄk: Go

package main import ( "bufio" "fmt" "os" ) type Position struct { y int x int } func isOutOfBounds(rows []string, pos Position) bool { if pos.y < 0 || pos.y >= len(rows) { return true } else if pos.x < 0 || pos.x >= len(rows[pos.y]) { return true } return false } func getNewDirection(dir Position) Position { if dir.y == -1 && dir.x == 0 { // north return Position{0, 1} // east } else if dir.y == 0 && dir.x == 1 { // east return Position{1, 0} // south } else if dir.y == 1 && dir.x == 0 { // south return Position{0, -1} // west } else if dir.y == 0 && dir.x == -1 { // west return Position{-1, 0} // north } panic("no supported direction") } func moveDirection(rows []string, pos, dir Position, visitMap map[int]map[int]int) (Position, Position, bool, int) { pos.y += dir.y pos.x += dir.x if isOutOfBounds(rows, pos) { return pos, dir, false, 0 } else if rows[pos.y][pos.x] == '#' || rows[pos.y][pos.x] == 'O' { pos.y -= dir.y pos.x -= dir.x return pos, getNewDirection(dir), true, 0 } // fmt.Println("Visiting", pos.y, pos.x) visitCount := visit(rows, pos, visitMap) return pos, dir, true, visitCount } func visit(rows []string, pos Position, visitMap map[int]map[int]int) int { if _, exist := visitMap[pos.y]; !exist { visitMap[pos.y] = make(map[int]int, len(rows[pos.y])) } visitMap[pos.y][pos.x]++ return visitMap[pos.y][pos.x] } func findStart(rows []string) (start, direction Position) { for y, row := range rows { for x, char := range row { if char == '^' { // direction up start.y, start.x = y, x direction.y, direction.x = -1, 0 return start, direction } } } panic("no start position") } func moveUntilDone(rows []string, pos, dir Position, visitMap map[int]map[int]int) bool { var moving bool = true posCount := 0 for moving { pos, dir, moving, posCount = moveDirection(rows, pos, dir, visitMap) if posCount > 4 { return true } } return false } func getPartOne(rows []string) (count int) { startPos, startDirection := findStart(rows) visitMap := make(map[int]map[int]int, len(rows)) visit(rows, startPos, visitMap) moveUntilDone(rows, startPos, startDirection, visitMap) for _, v := range visitMap { count += len(v) } return count } func deepCopySlice(original []string) []string { copy := make([]string, len(original)) for i, str := range original { copy[i] = str } return copy } func getPartTwo(_rows []string) (count int) { startPos, startDirection := findStart(_rows) visitMap := make(map[int]map[int]int, len(_rows)) visit(_rows, startPos, visitMap) moveUntilDone(_rows, startPos, startDirection, visitMap) cntChan := make(chan int) // CHANGE MADE HERE! for _y, _v := range visitMap { go func(y int, v map[int]int, rows []string) { rowCnt := 0 for x := range v { if startPos.y == y && startPos.x == x { continue } rows[y] = rows[y][:x] + "O" + rows[y][x+1:] newVisitMap := make(map[int]map[int]int, len(rows)) if moveUntilDone(rows, startPos, startDirection, newVisitMap) { rowCnt++ } rows[y] = rows[y][:x] + "." + rows[y][x+1:] } cntChan <- rowCnt }(_y, _v, deepCopySlice(_rows)) } for i := len(visitMap); i > 0; i-- { count += <-cntChan } return count } func getRows(filename string) []string { file, err := os.Open(filename) if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) var rows []string for scanner.Scan() { row := scanner.Text() rows = append(rows, row) } return rows } func main() { fmt.Println("Part one:", getPartOne(getRows("input.txt"))) fmt.Println("Part two:", getPartTwo(getRows("input.txt"))) }

Dold text

Snyggt, det sparar mig en hel sekund, trots att alla rader mÄste kopieras varje gÄng

PermalÀnk
Medlem
●

Dag: 7
SprÄk: C#

Hade problem med gÄrdagens problem (dag 6) - missade att hinder inte fÄr placeras flera gÄnger pÄ samma koordinat. Tog ett par timmar innan jag upptÀckte mitt misstag...
Idag gick det lÀttare. Jag var lite orolig medan jag implementerade part 1 att part 2 skulle vara stökig men det var bara att lÀgga till ett par rader i det hÀr fallet.

Lösningstid för part 2 ~200ms. Tog cirka sekunden med string concat som jag först anvÀnde.

internal static class December7 { public static readonly string _dataLocation = @data\input7dec.txt; public static long Question2() { long sum = 0; StreamReader reader = new StreamReader(_dataLocation); while (reader.ReadLine() is string line) { var first = line.Split(':'); long answer = long.Parse(first[0]); long[] numbers = first[1].Trim().Split(' ').Select(long.Parse).ToArray(); sum += addAndMultiply(numbers, 0, 0, answer); } return sum; } private static long addAndMultiply(long[] numbers, long i, long currentSum, long targetSum) { if (i == numbers.Length) { //Check if target sum was reached return currentSum == targetSum ? currentSum : 0; } long sum = currentSum + numbers[i]; long product = currentSum * numbers[i]; ; long concatenated = currentSum; long multiplier = 1; while (numbers[i] >= multiplier) { multiplier *= 10; } concatenated = concatenated * multiplier + numbers[i]; i++; long sum1 = 0; long sum2 = 0; long sum3 = 0; if (sum != 0 && sum <= targetSum) { sum1 = addAndMultiply(numbers, i, sum, targetSum); } if (product != 0 && product <= targetSum) { sum2 = addAndMultiply(numbers, i, product, targetSum); } if (concatenated != 0 && concatenated <= targetSum) { sum3 = addAndMultiply(numbers, i, concatenated, targetSum); } return (sum1 == targetSum || sum2 == targetSum || sum3 == targetSum) ? targetSum : 0; } }

Dold text
sprÄk
PermalÀnk
Medlem
●
Skrivet av Yoshman:

Just dag 2 Àr ju vÀldigt enkel att hantera med CUDA, Metal Compute eller motsvarande. DÄ man hÄller sig under 1024 rader Àr det trivialt att skriva en kernel som hanterar en rad per "CUDA-kÀrna" och sedan gör en enkel reduce pÄ slutet (det Àr vÀldigt enkelt sÄ lÀnge man hÄller sig pÄ ett block). Fungerar bÄde för del 1 och 2.

Men Àr rÀtt poÀnglöst dÄ det kommer ta flera tusentals gÄnger lÀngre att starta CUDA-kerneln och fÄ tillbaka resultatet jÀmfört med tiden sjÀlva berÀkningen tar. Totala tiden blir betydligt lÀgre om man bara hÄller sig pÄ CPU...

Hade antalet rader varit en miljon gÄnger fler eller sÄ hade det lönat sig att köra GPGPU.

Dold text

Problemet Àr inte bara att det tar tid att flytta data och starta en CUDA-kernel utan att det Àr vÀldigt fÄ berÀkningar per enhet data. Det Àndras inte om man har nÄgra miljoner gÄnger fler rader. Gör man sÄ fÄ berÀkningar Àr det bÀttre att stanna kvar i CPU om man ÀndÄ lÀst in alla data dÀr och har den nÀra i cache. Det gÄr ju att dela upp io, parsning och berÀkningar i lagom stora block och köra flera trÄdar. Med 16 kÀrnor testar man 1k element samtidigt.

Diskussionen handlade om datalayout. En rapport per register eller 1 element frÄn 64 olika rapporter per register. Jag trodde att skillnaden i hastighet skulle bli större eftersom jag inte kom pÄ nÄt bra sÀtt att undvika branching i inre loopen för del tvÄ med 64 olika rapporter per register. Skillnaden blev inte sÄ stor som jag trodde, 5us / 8us. SÄ jag hade fel, bÄda varianterna Àr effektiva. Det viktiga Àr hoppet frÄn 80us ner till 5us/8us. Men jag tycker nog att metoden att sprida ut en rapport Àr enklare och dessutom nÄgot snabbare.

Dold text
PermalÀnk
Medlem
●
Skrivet av WebbkodsFrilansaren:

Dag 7 Del 1 Diskussion

SÄ det Àr bruteforce av permutationer av operatorerna + och * trots allt? Det var det första jag tÀnkte pÄ sÄ fort jag gjorde min första tolkning av pusslet idag. Jag förstÄr inte varför PHP verkar vara sÄ minnesuselt sÄ fort en array vill lagra cirka 4 miljoner element? (antalet kombinationer av + och * i den rad med flest nummer, dvs., 12 nummer, dÀrmed 12-1 operatorer och dÀrmed !11 antal kombinationer).

Problemet jag verkar ha Àr inte lösningen i sig utan sjÀlva minneshanteringen (hÀr kommer verkligheten i kapp en nÀr man sluppit minneshantering i snart tre Ärs tid som Webbutvecklare) för att kunna köra lösningen.

Även i förrförra gĂ„rdagens Del 2 fick jag problemet med att jag skulle köra en foreach av reglerna pĂ„ varje kombination av numren, dvs., en foreach i en foreach och funktionen som genererade nummerkombinationer körde rekursivt och fick minnesbrist av samma skĂ€l: för mĂ„nga element i en array.

Btw, tror jag VSCode-minnet pÄ flera GB berodde mest pÄ att jag försökte var_dumpa (skriva ut) i terminalen ~4 miljoner element snarare Àn den faktiska minneslagringen pÄ ~1024 MB för hela arrayen.

Dold text

Mvh,
WKF.

Ett tips kan vara att anvĂ€nda papper och penna. Börja med enklast möjliga problem. Ett mĂ„l och ett nummer. Hur löser du det problemet? LĂ€gg till ett nummer till sĂ„ du har tvĂ„ nummer och en tom plats mellan de som kan vara + eller ×. Hur löser du problemet dĂ„? Vad hĂ€nder nĂ€r du har tre nummer? Rita upp problemet som ett trĂ€d. Ett nummer per nivĂ„ med tvĂ„ förgreningar, en för + och en för ×. Finns det situationer som kan uppstĂ„ som gör det meningslöst att fortsĂ€tta ner i en viss förgrening? I del tvĂ„ Ă€r det tre förgreningar per nummer.

Dold text
PermalÀnk
Medlem ★
●

Dag: 4
SprÄk: C

#include <stdio.h> static inline int check_xmas(const char a[140][144], const int i, const int j) { int count = 0; // check right if ((a[i][j] == 'X' && a[i][j+1] == 'M' && a[i][j+2] == 'A' && a[i][j+3] =='S') || (a[i][j] == 'S' && a[i][j+1] == 'A' && a[i][j+2] == 'M' && a[i][j+3] =='X')) { count++; } // check down if ((a[i][j] == 'X' && a[i+1][j] == 'M' && a[i+2][j] == 'A' && a[i+3][j] =='S') || (a[i][j] == 'S' && a[i+1][j] == 'A' && a[i+2][j] == 'M' && a[i+3][j] =='X')) { count++; } // diag - down right if ((a[i][j] == 'X' && a[i+1][j+1] == 'M' && a[i+2][j+2] == 'A' && a[i+3][j+3] =='S') || (a[i][j] == 'S' && a[i+1][j+1] == 'A' && a[i+2][j+2] == 'M' && a[i+3][j+3] =='X')) { count++; } // diag - up left if ((a[i+3][j] == 'X' && a[i+2][j+1] == 'M' && a[i+1][j+2] == 'A' && a[i][j+3] =='S') || (a[i+3][j] == 'S' && a[i+2][j+1] == 'A' && a[i+1][j+2] == 'M' && a[i][j+3] =='X')) { count++; } return count; } static inline int check_mas(const char a[140][144], const int i, const int j) { if (((a[i][j] == 'M' && a[i+1][j+1] == 'A' && a[i+2][j+2] == 'S') || (a[i][j] == 'S' && a[i+1][j+1] == 'A' && a[i+2][j+2] == 'M')) && ((a[i+2][j] == 'M' && a[i+1][j+1] == 'A' && a[i][j+2] == 'S') || (a[i+2][j] == 'S' && a[i+1][j+1] == 'A' && a[i][j+2] == 'M'))) { return 1; } return 0; } int main(int argc, char *argv[]) { FILE *fp; int i = 0, j = 0, part1 = 0, part2 = 0; char a[140][144] = {0}; // Add 4 bytes extra per row, avoids loop range // checks at the cost of some minor extra compute... fp = fopen("input.txt", "r"); while (fgets(a[i], 142, fp) != NULL) { // last 2 bytes are don't care: '\n\0' i++; } fclose(fp); for (i = 0; i < 140; i++) { for (j = 0; j < 140; j++) { part1 += check_xmas(a, i, j); part2 += check_mas(a, i, j); } } printf("part 1: %d\n", part1); printf("part 2: %d\n", part2); return 0; }

Dold text

Plain old simpel C igen. Valde att lÀgga till nÄgra onödiga bytes (och dÀrmed checks) per rad för att slippa range-checks i for-loopen.

Visa signatur

Citera mig för svar.
Arch Linux

PermalÀnk
Medlem
●

Dag: 8
SprÄk: Java
Lösning: GitHub

Största problemet idag var att förstÄ vad han menade, men koden var ganska lÀtt.

PermalÀnk
Medlem ★
●
Skrivet av ViLue:

Dag: 8
SprÄk: Java
Lösning: GitHub

Största problemet idag var att förstÄ vad han menade, men koden var ganska lÀtt.

InstÀmmer.
VÀldigt rörig beskrivning (för mig) dÀr jag först trodde det handlade om att rÀkna ut vilka antenner som kunde bilda ett par - utan att nÄgon annan antenn blockerade "line of sight" sÄ att sÀga.

InsÄg sedan att det inte alls handlade om detta och allt blev genast mycket enklare

Dold text

Dag: 8
SprÄk: C#
Dag 8

Del 1: ~0.4ms
Del 2: ~0.6ms

KÀnner mig dÀremot smÄtt besegrad sÄ jag behövde googla pÄ formler för första gÄngen. Nackdelen med att jobba med "vanlig" utveckling

public class Solver { private struct Vector() : IEquatable<Vector> { public Vector(int x, int y) : this() { X = x; Y = y; } public Vector(Vector v) : this() { X = v.X; Y = v.Y; } public int X { get; private set; } public int Y { get; private set; } public static Vector operator - (Vector a, Vector b) { return new Vector(a.X - b.X, a.Y - b.Y); } public static Vector operator + (Vector a, Vector b) { return new Vector(a.X + b.X, a.Y + b.Y); } public static bool operator ==(Vector a, Vector b) { return a.X == b.X && a.Y == b.Y; } public static bool operator !=(Vector a, Vector b) { return a == b == false; } public bool Equals(Vector other) { return X == other.X && Y == other.Y; } public override bool Equals(object? obj) { return obj is Vector other && Equals(other); } public override int GetHashCode() { return HashCode.Combine(X, Y); } } private struct Node(Vector position, string value) { public Vector Position { get; set; } = position; public string Value { get; set; } = value; } private static Node[,] GetGrid(string input) { string[] lines = input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); int height = lines.Length; int width = lines[0].Length; var grid = new Node[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { grid[y, x] = new Node(new Vector(x, y), lines[y][x].ToString()); } } return grid; } private static Vector GetDistanceBetweenNodes(Node a, Node b) { return new Vector(a.Position - b.Position); } private static Node? GetNodeAtDistance(Vector distance, Node otherNode, Node[,] grid) { var pos = otherNode.Position + distance; if (pos.X < 0 || pos.X >= grid.GetLength(1)) { return null; } if (pos.Y < 0 || pos.Y >= grid.GetLength(0)) { return null; } return grid[pos.Y, pos.X]; } private static List<Node> GetAllNodesInDirectionTowardsB(Node a, Node b, Node[,] grid) { var delta = b.Position - a.Position; var gcd = GCD(Math.Abs(delta.X), Math.Abs(delta.Y)); int stepX = delta.X / gcd; int stepY = delta.Y / gcd; int x = a.Position.X; int y = a.Position.Y; var currentNode = a; var nodes = new List<Node>(); while (true) { nodes.Add(currentNode); x += stepX; y += stepY; if ((x < 0 || x >= grid.GetLength(1)) || (y < 0 || y >= grid.GetLength(0))) break; currentNode = grid[y, x]; } return nodes; } private static int GCD(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } public static int Run_PartOne(string input) { var grid = GetGrid(input); var distinctNodes = new Dictionary<string, List<Node>>(); var antiNodes = new List<Node>(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { var node = grid[y, x]; if (node.Value == ".") continue; if (!distinctNodes.TryAdd(node.Value, [node])) { distinctNodes[node.Value].Add(node); } } } foreach (var kvp in distinctNodes) { foreach (var node in kvp.Value) { foreach (var node2 in kvp.Value) { if (node.Position == node2.Position) continue; var distance = GetDistanceBetweenNodes(node, node2); var node3 = GetNodeAtDistance(distance, node, grid); if (node3.HasValue) { antiNodes.Add(node3.Value); } } } } return antiNodes.DistinctBy(x => x.Position).Count(); } public static int Run_PartTwo(string input) { var grid = GetGrid(input); var distinctNodes = new Dictionary<string, List<Node>>(); var antiNodes = new List<Node>(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { var node = grid[y, x]; if (node.Value == ".") continue; if (!distinctNodes.TryAdd(node.Value, [node])) { distinctNodes[node.Value].Add(node); } } } foreach (var kvp in distinctNodes) { foreach (var node in kvp.Value) { foreach (var node2 in kvp.Value) { if (node.Position == node2.Position) continue; var nodes = GetAllNodesInDirectionTowardsB(node, node2, grid); foreach (var node3 in nodes) { antiNodes.Add(node3); } } } } return antiNodes.DistinctBy(x => x.Position).Count(); } }

Dold text
Visa signatur

NZXT H510 Flow MSI B450 Tomahawk MAX
AMD Ryzen 5800X3D RX 7900XTX Kingston Fury 64GB
LG C2 42" 4K@120Hz AOC Q27G2U 1440P@144Hz

PermalÀnk
Datavetare ★
●

Dag: 7
SprÄk: Rust

part 1: 31 ”s (M3), 141 ”s (Orange Pi 5)
part 2: 41 ”s (M3), 235 ”s (Orange Pi 5)

use std::fs; pub const INPUT_FILE: &str = "input.txt"; type Number = i64; pub fn read_result_and_numbers(path: &str) -> Vec<(Number, Vec<Number>)> { fs::read_to_string(path) .expect("Failed to read input file") .lines() .map(|line| { let parts: Vec<&str> = line.split(':').collect(); let test_value = parts[0].parse().expect("Failed to parse number"); let numbers = parts[1] .split_whitespace() .map(|num| num.parse().expect("Failed to parse number")) .rev() .collect(); (test_value, numbers) }) .collect() } pub fn part1(equations: &[(Number, Vec<Number>)]) -> Number { total_calibration_result(equations, |result, numbers| { is_equation_valid(result, numbers, false) }) } pub fn part2(equations: &[(Number, Vec<Number>)]) -> Number { total_calibration_result(equations, |result, numbers| { is_equation_valid(result, numbers, true) }) } pub fn total_calibration_result( equations: &[(Number, Vec<Number>)], pred: impl Fn(Number, &[Number]) -> bool, ) -> Number { equations .iter() .filter(|(result, numbers)| pred(*result, &numbers)) .map(|(result, _)| result) .sum() } fn is_equation_valid(result: Number, numbers: &[Number], use_concat: bool) -> bool { if numbers.is_empty() { return false; } let rhs = numbers[0]; let lhs = &numbers[1..]; (lhs.len() == 0 && result == rhs) || (use_concat && { let lhs_factor = next_larger_power_of_10(rhs); // lhs || rhs -> lhs * lhs_factor + rhs is_factor(result - rhs, lhs_factor) && is_equation_valid(result / lhs_factor, lhs, true) }) || (is_factor(result, rhs) && is_equation_valid(result / rhs, lhs, use_concat)) || (result >= rhs && is_equation_valid(result - rhs, lhs, use_concat)) } fn is_factor(product: Number, number: Number) -> bool { product % number == 0 } fn next_larger_power_of_10(n: Number) -> Number { let mut factor = 10; while factor <= n { factor *= 10; } factor } fn main() { let equations = read_result_and_numbers(&INPUT_FILE); println!("part 1: {}", part1(&equations)); println!("part 2: {}", part2(&equations)); }

Dold text

Dag: 8
SprÄk: Rust

part 1: 7 ”s (M3), 16 ”s (Orange Pi 5)
part 2: 7 ”s (M3), 19 ”s (Orange Pi 5)

use std::fs; use ahash::AHashMap; use itertools::Itertools; pub const INPUT_FILE: &str = "input.txt"; #[derive(Debug)] pub struct Vec2D { x: i32, y: i32, } pub type AntennaPositions = Vec<Vec2D>; pub fn read_map_dimensions_and_antenna_positions( path: &str, ) -> ((i32, i32), Vec<AntennaPositions>) { let content = fs::read_to_string(path).expect("Failed to read file"); let mut antenna_positions: AHashMap<char, AntennaPositions> = AHashMap::new(); let mut height = 0; let mut width = 0; for (y, line) in content.lines().enumerate() { height += 1; width = line.len() as i32; for (x, ch) in line.chars().enumerate() { if ch != '.' { antenna_positions .entry(ch) .or_insert_with(Vec::new) .push(Vec2D { x: x as i32, y: y as i32, }); } } } ((width, height), antenna_positions.into_values().collect()) } pub fn part1(width: i32, height: i32, antenna_positions: &[AntennaPositions]) -> u32 { let mut antinodes = vec![0u64; height as usize]; let mut mark_antinode = |a: &Vec2D, b: &Vec2D| { let x = 2 * b.x - a.x; let y = 2 * b.y - a.y; if x >= 0 && x < width && y >= 0 && y < height { antinodes[y as usize] |= 1 << x; } }; for positions in antenna_positions { for pair in positions.iter().combinations(2) { mark_antinode(pair[0], pair[1]); mark_antinode(pair[1], pair[0]); } } antinodes.into_iter().map(|row| row.count_ones()).sum() } pub fn part2(width: i32, height: i32, antenna_positions: &[AntennaPositions]) -> u32 { let mut antinodes = vec![0u64; height as usize]; let mut mark_antinode_and_harmonics = |a: &Vec2D, b: &Vec2D| { let dx = b.x - a.x; let dy = b.y - a.y; let mut x = b.x; let mut y = b.y; while x >= 0 && x < width && y >= 0 && y < height { antinodes[y as usize] |= 1 << x; x += dx; y += dy; } }; for positions in antenna_positions { for pair in positions.iter().combinations(2) { mark_antinode_and_harmonics(pair[0], pair[1]); mark_antinode_and_harmonics(pair[1], pair[0]); } } antinodes.into_iter().map(|row| row.count_ones()).sum() } fn main() { let ((width, height), antenna_positions) = read_map_dimensions_and_antenna_positions(&INPUT_FILE); println!("part 1: {}", part1(width, height, &antenna_positions)); println!("part 2: {}", part2(width, height, &antenna_positions)); }

Dold text

AngÄende körtider. NÀr dessa kommer vÀl under 1ms Àr det lite "sanning med modifikation" att man fÄ de tider som listas ovan. Dessa Àr vad som rapporteras av Rust benchmark-system "criterion" dÀr det kan se ut t.ex. sÄ hÀr

fn part1_benchmark(c: &mut Criterion) { let equations = read_result_and_numbers(&INPUT_FILE); c.bench_function("part 1", |b| b.iter(|| part1(black_box(&equations)))); }

Tiden man fÄ dÀr och tiden man fÄr om man istÀllet gör sÄ hÀr

let equations = read_result_and_numbers(&INPUT_FILE); let start_part1 = Instant::now(); let result_part1 = part1(&equations); let duration_part1 = start_part1.elapsed(); println!("part 1: {}", result_part1); println!( "Execution time for part 1: {:.3} ”s", duration_part1.as_secs_f64() * 1_000_000.0 );

skiljer sig en hel del för vÀldigt korta körtider. Gör man ovan i en loop blir det samma sak, men första körningen Àr alltid klart lÄngsammare (och lÀr skilja Ànnu mer nÀr man kör t.ex. med JVM som har "warmup" time).

Tar det 10-tals ms ger dock bÄda metoderna vÀldigt snarlika resultat.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Datavetare ★
●
Skrivet av jclr:

Problemet Àr inte bara att det tar tid att flytta data och starta en CUDA-kernel utan att det Àr vÀldigt fÄ berÀkningar per enhet data. Det Àndras inte om man har nÄgra miljoner gÄnger fler rader. Gör man sÄ fÄ berÀkningar Àr det bÀttre att stanna kvar i CPU om man ÀndÄ lÀst in alla data dÀr och har den nÀra i cache. Det gÄr ju att dela upp io, parsning och berÀkningar i lagom stora block och köra flera trÄdar. Med 16 kÀrnor testar man 1k element samtidigt.

Diskussionen handlade om datalayout. En rapport per register eller 1 element frÄn 64 olika rapporter per register. Jag trodde att skillnaden i hastighet skulle bli större eftersom jag inte kom pÄ nÄt bra sÀtt att undvika branching i inre loopen för del tvÄ med 64 olika rapporter per register. Skillnaden blev inte sÄ stor som jag trodde, 5us / 8us. SÄ jag hade fel, bÄda varianterna Àr effektiva. Det viktiga Àr hoppet frÄn 80us ner till 5us/8us. Men jag tycker nog att metoden att sprida ut en rapport Àr enklare och dessutom nÄgot snabbare.

Dold text

Var kanske otydligt vad jag svarade pÄ, egentligen mer ett svar pÄ frÄgan du initialt svarade pÄ om det finns "bÀttre sÀtt". GPGPU Àr i.o.f.s. i grunden ocksÄ bara en form av SIMD.

Eller Nvidia gillar inte riktigt att man kallar det SIMD, de vill kalla det SIMT (Single Instruction Multiple Threads) dÄ GPUer (sÄ hÀr lÄngt) typiskt har rÀtt mycket mer flexibilitet i hur de kan göra scatter/gather pÄ olika GPU-trÄdar jÀmfört med scatter/gather som moderna CPUer har.

Att det Àr fÄ berÀkningar per trÄd Àr inte ett problem. TÀnk pÄ vad en GPU gör med grafik, den kör typiskt vÀldigt enkla berÀkningar "per trÄd" i vertex-shaders / fragment-shaders. Och antalet gÄnger man kör en specifik fragment-shader-trÄd blir ju typiskt en bra bit över 10 miljoner gÄnger per frame i ett modernt spel i 4k upplösning.

OvanpÄ det Àr just AoC problemen ofta rÀtt lÀtt att "vÀxa" per trÄd. Fallet vi diskuterar hÀr kan ju vÀxas genom att varje trÄd tar, sÀg 10, rader var. Statistiskt skulle de rimligen ocksÄ göra sÄ att genomsnittslÀngden pÄ antalet saker att hantera kommer fÄ mindre relativ spridning Àn nÀr man lÄter en trÄd ta en rad.

Är dĂ€rför frĂ€mst kostanden att starta kernel och fĂ„ tillbaka svaret som Ă€r flaskhals nĂ€r man gĂ„r en bra bit under 1ms i totaltid, framförallt nĂ€r man anvĂ€nder dGPU (iGPUer har rejĂ€lt mindre overhead).

Huvudvinsten med att köra med GPGPU jÀmfört med handknackad SIMD-kod Àr att den förra Àr vÀldigt lik "vanlig" kod, fast skriven för en invokering av en SIMD-lane. Den senare blir ofta "nÀra nog magi...".

FÄr testa att skiva nÄgon av problemen med Metal Compute eller CUDA
Men man fÄr nog vÀxa problemet för att fÄ ett vettig körtid som inte till >99 % bestÄr att launch/host-sync delen...

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Medlem ★
●

Dag: 8
SprÄk: Python

import numpy as np from itertools import combinations a = np.array([[c for c in l.strip()] for l in open("input08.txt", "r").readlines()]) p1 = set() p2 = set() def part1(x1, y1, x2, y2): dx, dy = x2 - x1, y2 - y1 x1, y1 = x1 - dx, y1 - dy x2, y2 = x2 + dx, y2 + dy if 0 <= x1 < a.shape[0] and 0 <= y1 < a.shape[1]: p1.add((x1, y1)) if 0 <= x2 < a.shape[0] and 0 <= y2 < a.shape[1]: p1.add((x2, y2)) def part2(x1, y1, x2, y2): dx, dy = x2 - x1, y2 - y1 while 0 <= x1 < a.shape[0] and 0 <= y1 < a.shape[1]: p2.add((x1, y1)) x1, y1 = x1 - dx, y1 - dy while 0 <= x2 < a.shape[0] and 0 <= y2 < a.shape[1]: p2.add((x2, y2) ) x2, y2 = x2 + dx, y2 + dy for c in np.unique(a[a != '.']): for (x1, y1), (x2, y2) in combinations(zip(*np.where(a == c)), 2): part1(x1, y1, x2, y2) part2(x1, y1, x2, y2) print(len(p1), len(p2))

Dold text
PermalÀnk
Datavetare ★
●
Skrivet av Pamudas:

InstÀmmer.
VÀldigt rörig beskrivning (för mig) dÀr jag först trodde det handlade om att rÀkna ut vilka antenner som kunde bilda ett par - utan att nÄgon annan antenn blockerade "line of sight" sÄ att sÀga.

InsÄg sedan att det inte alls handlade om detta och allt blev genast mycket enklare

Dold text

Dag: 8
SprÄk: C#
Dag 8

Del 1: ~9ms
Del 2: ~1ms

KÀnner mig dÀremot smÄtt besegrad sÄ jag behövde googla pÄ formler för första gÄngen. Nackdelen med att jobba med "vanlig" utveckling

public class Solver { private struct Vector() : IEquatable<Vector> { public Vector(int x, int y) : this() { X = x; Y = y; } public Vector(Vector v) : this() { X = v.X; Y = v.Y; } public int X { get; private set; } public int Y { get; private set; } public static Vector operator - (Vector a, Vector b) { return new Vector(a.X - b.X, a.Y - b.Y); } public static Vector operator + (Vector a, Vector b) { return new Vector(a.X + b.X, a.Y + b.Y); } public static bool operator ==(Vector a, Vector b) { return a.X == b.X && a.Y == b.Y; } public static bool operator !=(Vector a, Vector b) { return a == b == false; } public bool Equals(Vector other) { return X == other.X && Y == other.Y; } public override bool Equals(object? obj) { return obj is Vector other && Equals(other); } public override int GetHashCode() { return HashCode.Combine(X, Y); } } private struct Node(Vector position, string value) { public Vector Position { get; set; } = position; public string Value { get; set; } = value; } private static Node[,] GetGrid(string input) { string[] lines = input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); int height = lines.Length; int width = lines[0].Length; var grid = new Node[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { grid[y, x] = new Node(new Vector(x, y), lines[y][x].ToString()); } } return grid; } private static Vector GetDistanceBetweenNodes(Node a, Node b) { return new Vector(a.Position - b.Position); } private static Node? GetNodeAtDistance(Vector distance, Node otherNode, Node[,] grid) { var pos = otherNode.Position + distance; if (pos.X < 0 || pos.X >= grid.GetLength(1)) { return null; } if (pos.Y < 0 || pos.Y >= grid.GetLength(0)) { return null; } return grid[pos.Y, pos.X]; } private static List<Node> GetAllNodesInDirectionTowardsB(Node a, Node b, Node[,] grid) { var delta = b.Position - a.Position; var gcd = GCD(Math.Abs(delta.X), Math.Abs(delta.Y)); int stepX = delta.X / gcd; int stepY = delta.Y / gcd; int x = a.Position.X; int y = a.Position.Y; var currentNode = a; var nodes = new List<Node>(); while (true) { nodes.Add(currentNode); x += stepX; y += stepY; if ((x < 0 || x >= grid.GetLength(1)) || (y < 0 || y >= grid.GetLength(0))) break; currentNode = grid[y, x]; } return nodes; } private static int GCD(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } public static int Run_PartOne(string input) { var grid = GetGrid(input); var distinctNodes = new Dictionary<string, List<Node>>(); var antiNodes = new List<Node>(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { var node = grid[y, x]; if (node.Value == ".") continue; if (!distinctNodes.TryAdd(node.Value, [node])) { distinctNodes[node.Value].Add(node); } } } foreach (var kvp in distinctNodes) { foreach (var node in kvp.Value) { foreach (var node2 in kvp.Value) { if (node.Position == node2.Position) continue; var distance = GetDistanceBetweenNodes(node, node2); var node3 = GetNodeAtDistance(distance, node, grid); if (node3.HasValue) { antiNodes.Add(node3.Value); } } } } return antiNodes.DistinctBy(x => x.Position).Count(); } public static int Run_PartTwo(string input) { var grid = GetGrid(input); var distinctNodes = new Dictionary<string, List<Node>>(); var antiNodes = new List<Node>(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { var node = grid[y, x]; if (node.Value == ".") continue; if (!distinctNodes.TryAdd(node.Value, [node])) { distinctNodes[node.Value].Add(node); } } } foreach (var kvp in distinctNodes) { foreach (var node in kvp.Value) { foreach (var node2 in kvp.Value) { if (node.Position == node2.Position) continue; var nodes = GetAllNodesInDirectionTowardsB(node, node2, grid); foreach (var node3 in nodes) { antiNodes.Add(node3); } } } } return antiNodes.DistinctBy(x => x.Position).Count(); } }

Dold text

HÀr Àr ett exempel nÀr körtiden, som jag gissar du mÀtte med "Stopwatch"?, inte alls blir rÀtt. Det Àr betydligt snabbare om man tar bort "warmup". .NET verkar ha lÄngt kortare warmup jÀmfört med typiska JVM:er, redan körningen av part 2 verkar vara "ljummen" dÄ det rimligen borde ta lÀngre tid Àn part 1.

Kör man i en loop fÄr jag ca 90”s i del 1 och ca 310”s i del 2 med din lösning pÄ en M3 och .NET core 8.

I första varvet fÄr man ~5ms och 1ms pÄ samma dator...

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Medlem ★
●

Dag 8, C#

Ganska rÀttframt idag, helgproblemen brukar ju i regel vara lite mer att bita i.

using System.Diagnostics; Stopwatch sw = Stopwatch.StartNew(); var lines = File.ReadAllLines("input.txt"); (HashSet<(int x, int y)> part1, HashSet<(int x, int y)> part2, int maxpos) = ([], [], lines.Length); Dictionary<(int x, int y), char> grid = []; for (int x = 0; x < lines.Length; x++) for (int y = 0; y < lines[x].Length; y++) if (lines[x][y] != '.') grid[(x, y)] = lines[x][y]; foreach (var pos in grid) { foreach (var antenna in grid.Where(x => x.Key != pos.Key && x.Value == pos.Value)) { if (getAnte(out (int x, int y) ante, pos.Key, antenna.Key, maxpos)) part1.Add(ante); part2.UnionWith(getResonance(pos.Key, antenna.Key, maxpos)); } } sw.Stop(); Console.WriteLine("Part 1: " + part1.Count); Console.WriteLine("Part 2: " + part2.Count); Console.WriteLine("Total time: " + sw.Elapsed); static bool inRange((int x, int y) pos, int max) => (pos.x < max && pos.y < max && pos.x >= 0 && pos.y >= 0); static (int x, int y) calculateDiff((int x, int y) me, (int x, int y) them) { (int x, int y) diff = (Math.Abs(me.x - them.x), Math.Abs(me.y - them.y)); if (them.x > me.x) diff.x = -1 * diff.x; if (them.y > me.y) diff.y = -1 * diff.y; return diff; } static bool getAnte(out (int x, int y) val, (int x, int y) me, (int x, int y) them, int max) { var diff = calculateDiff(me, them); val = (me.x + diff.x, me.y + diff.y); return inRange(val, max); } static IEnumerable<(int x, int y)> getResonance((int x, int y) me, (int x, int y) them, int max) { HashSet<(int x, int y)> retval = []; var diff = calculateDiff(me, them); while (inRange(me, max)) { retval.Add(me); me = (me.x + diff.x, me.y + diff.y); } return retval; }

Dold text
kan förstÄs inte lÄta bli att gÄ tillbaks och optimera lite
Visa signatur

The power of GNU compiles you!
"Often statistics are used as a drunken man uses lampposts -- for support rather than illumination."