🌟 Advent of Code (AoC) 2024 🌟

PermalÀnk
Medlem ★
●
Skrivet av jclr:

Jag kan inte komma pÄ hur man skulle kunna göra det effektivt pÄ det sÀtt du beskriver. Det gÄr inte att göra en gather pÄ 64 bytes alltsÄ mÄste man under parsning arrangera om datan. Hur hÄller du dÄ koll pÄ lÀngden pÄ varje report? Och hur skulle del 2 fungera?

Det enda man ska göra som har med tvÄ element i samma vektor Àr en subtraktion av nÀrliggande element. Det Àr en 3 latency vpermb för att skifta vektorn ett steg.

HÀr kommer ett lite mer detaljerat exempel hur man skulle kunna göra (skippar optimerad parsning):

AnvÀnder de tvÄ första exemplen frÄn uppgiften

LÀs in textfilen till en 1000 x 8 byte array sÄ att man fÄr 0 padding pÄ slutet.

[7 6 4 2 1 0 0 0 1 2 7 8 9 0 0 0 ...]

Flytta 64 bytes till ett vektor register

<7 6 4 2 1 0 0 0 1 2 7 8 9 0 0 0 ...>

LÀngden - 1 för varje enskilt report behövs senare och den fÄr man genom att anvÀnda vektor registret som ett 8 x 64 bitars och ta 7 - (lzcnt( 64 bitar ) >> 3). (leading zero count)

<4 4 ...>

Skifta en kopia av registret ett steg och subtrahera. (? Àr skrÀp frÄn nÀsta report som kommer maskas bort senare)

r1 = <7 6 4 2 1 0 0 0 1 2 7 8 9 0 0 0 ...> r2 = <6 4 2 1 0 0 0 1 2 7 8 9 0 0 0 ? ...> r1 - r2 = <1 2 2 1 1 0 0 -1 -1 -5 -1 -1 9 0 0 ? ...>

Skapa en mask som maskar bort allt utom de bitar man Àr intresserat av t.ex. genom att kombinera element i r2 som inte Àr 0 med en 7f7f... mask som maskar bort högsta byten i varje report.

<1 2 2 1 1 0 0 -1 -1 -5 -1 -1 9 0 0 ? ...> <T T T T _ _ _ _ T T T T _ _ _ _ ...>

Nu kan man göra tvÄ maskade compares som kollar om elementen Àr mellan 1 och 3 eller mellan -1 och -3.

<1 2 2 1 1 0 0 -1 -1 -5 -1 -1 9 0 0 ? ...> cmp1 = <T T T T _ _ _ _ _ _ _ _ _ _ _ _ ...> cmp2 = <_ _ _ _ _ _ _ _ T  _ T T _ _ _ _ ...>

Sen flyttar man tillbaka maskerna till tvÄ vektor register. Det sÀtter alla bitar till 1 om masken Àr T. AlltsÄ fÄr man vÀrdet -1 pÄ bytes som var sanna och 0 pÄ de andra

<-1 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0 ...> < 0 0 0 0 0 0 0 0 -1 0 -1 -1 0 0 0 0 ...>

Genom att tolka registret som ett 8 x 64 bit igen kan man ta en popcount för rÀkna antalet satta bitar per report.

cnt1 = <32 0 ...> cnt2 = <0 24 ...>

Om man nu gör en compare mellan antalet satta bitar i bÄda registren och lÀngden man sparade tidigare gÄnger 8 sÄ fÄr man fram en mask som visar vilka reports som Àr safe.

len = < 4 4 ...> x 8 = <32 32 ...> cnt1 = <32 0 ...> cnt2 = <0 24 ...> m1 = <T  _ ...> m2 = <_ _ ...>

Genom att göra en popcount pÄ m1 och m2 sÄ fÄr man antalet Safe reports.

Del tvÄ Àr i princip samma sak fast istÀllet för att lÀsa in 8 reports sÄ tar man en Ät gÄngen. Antingen genom en maskad load eller genom en inre loop som skiftar ner registret 8 bytes Ät gÄngen.

<1 3 2 4 5 0 0 0 0 ...>

Sen gör man en permute som sprider ut elementen i alla möjliga kombinationer av 7 element.

<3 2 4 5 0 0 0 0 1 2 4 5 0 0 0 0 1 3 4 5 0 0 0 0...>

Skillnaden frÄn del 1 Àr att man sen kollar om man fÄr minst en trÀff i antingen m1 eller m2 istÀllet för att rÀkna ihop alla trÀffar.

Det gÄr sÀkert att göra nÄgot smartare men att lösa problemet utan simd tar < 100us sÄ knappast vÀrt att lÀgga ner tid pÄ den hÀr typen av lösning.

Dold text

Jag förstÄr vad du vill göra och, ja, det gÄr att göra, men det Àr fel

Koden skulle bli mycket krÄngligare Àn om du bara skrev rak kod som loopade över de Ätta records du fÄr in i ett register. Du mÄste till exempel skriva kod som kör bÄda grenarna av din if else och har k-register som kontrollerar vilka element som instruktionerna pÄverkar. Har du en if else i en if else-gren mÄste du se till att köra bÄda grenar dÀr ocksÄ. Det blir snabbt kombinatoriskt jobbigt. Med en maximal parallellitet pÄ 8 sÄ jag Àr tveksam till om det skulle gÄ snabbare över huvud taget att köra AVX512.

Den stora poÀngen med SIMD Àr ju att kunna operera pÄ mycket data i taget. Ta exempelvis ett filter som rÀknar ut nÄgot baserat pÄ R-, G- och B-vÀrden. DÄ ser man till att ladda R, G och B i olika register och kan köra 64 berÀkningar parallellt. Om inte indata passar i den formen det kom in, dÄ arrangerar man om det till nÄgot som passar bÀttre.

Det gÄr att göra som du föreslÄr men jag tror inte du skulle se den uppsnabbning du hoppas pÄ. Men, men, det skulle vara exotiskt och det var ju dÀr diskussionen startade

Dold text
PermalÀnk
Medlem
●

Dag: 4
SprÄk: Rust

const WIDTH: usize = 140; const XMAS: usize = 4; fn main() -> Result<(), Box<dyn std::error::Error>> { let content: Vec<_> = std::fs::read_to_string("input.txt")? .chars() .filter(|c| c.is_alphabetic()) .collect(); println!( "Part 1: {}", check_rows(&content, XMAS) + check_column(&content, XMAS) + check_diagonal_left(&content, XMAS) + check_diagonal_right(&content, XMAS) // 2414 ); println!("Part 2: {}", x_mas(&content, 3)); // 1871 Ok(()) } fn check_rows(content: &[char], word_width: usize) -> u32 { let mut sum = 0; for chunk in content.chunks(WIDTH) { for word in chunk.windows(word_width) { sum += match_xmas(word) as u32; } } sum } fn check_column(content: &[char], word_width: usize) -> u32 { summarize(content, word_width, |chunk| { (0..WIDTH).fold(0, |acc, i| { acc + match_xmas(&[ chunk[i], chunk[i + WIDTH], chunk[i + 2 * WIDTH], chunk[i + 3 * WIDTH], ]) as u32 }) }) } fn check_diagonal_left(content: &[char], word_width: usize) -> u32 { summarize(content, word_width, |chunk| { const ADD: usize = WIDTH - 1; (word_width - 1..WIDTH).fold(0, |acc, i| { acc + match_xmas(&[ chunk[i], chunk[i + ADD], chunk[i + 2 * ADD], chunk[i + 3 * ADD], ]) as u32 }) }) } fn check_diagonal_right(content: &[char], word_width: usize) -> u32 { summarize(content, word_width, |chunk| { const ADD: usize = WIDTH + 1; (0..WIDTH - word_width + 1).fold(0, |acc, i| { acc + match_xmas(&[ chunk[i], chunk[i + ADD], chunk[i + 2 * ADD], chunk[i + 3 * ADD], ]) as u32 }) }) } fn x_mas(content: &[char], word_width: usize) -> u32 { summarize(content, word_width, |chunk| { const ADD: usize = WIDTH + 1; (0..WIDTH - word_width + 1).fold(0, |acc, i| { acc + (match_mas(&[chunk[i], chunk[i + ADD], chunk[i + 2 * ADD]]) && match_mas(&[chunk[i + 2 * WIDTH], chunk[i + ADD], chunk[i + 2]])) as u32 }) }) } fn summarize<T: Fn(&[char]) -> u32>(content: &[char], word_width: usize, func: T) -> u32 { (0..content.len()) .step_by(WIDTH) .filter(|i| i + word_width * WIDTH <= content.len()) .fold(0, |accum, i| { accum + func(&content[i..i + word_width * WIDTH]) }) } fn match_mas(word: &[char]) -> bool { word == &['M', 'A', 'S'] || word == &['S', 'A', 'M'] } fn match_xmas(word: &[char]) -> bool { word == &['X', 'M', 'A', 'S'] || word == &['S', 'A', 'M', 'X'] }

Dold text
Visa signatur

AMD Ryzen 3700X, Gigabyte Elite X570, 32GB Ballistix Sport, NH-D15S, SAPPHIRE NITRO+ RX 7900 XTX, Corsair AX760, LG OLED C2 42"

PermalÀnk
Medlem
●

Dag: 4
SprÄk: Scala

Skrev en simd/swar variant av del 1. AnvÀnder först simd för att spara ner bitmasker för varje enskild bokstav i XMAS och sen swar för att kolla 64 positioner parallellt. Den sista delen behöver optimeras för att ta bort en massa upprepade berÀkningar och sen behöver koden stÀdas upp lite allmÀnt ocksÄ.

object Day04Bsimd: private val path = """4.txt""" private val data = Using.resource(Source.fromFile(path))(_.getLines.mkString) private val bytes = data.getBytes private val cols = 140 private val rows = 140 private val xa = Array.ofDim[Long](cols * ((rows + 64) / 64 + 2)) private val ma = Array.ofDim[Long](cols * ((rows + 64) / 64 + 2)) private val aa = Array.ofDim[Long](cols * ((rows + 64) / 64 + 2)) private val sa = Array.ofDim[Long](cols * ((rows + 64) / 64 + 2)) private val bsp = ByteVector.SPECIES_PREFERRED private val xv = ByteVector.broadcast(bsp, 'X'.toByte) private val mv = ByteVector.broadcast(bsp, 'M'.toByte) private val av = ByteVector.broadcast(bsp, 'A'.toByte) private val sv = ByteVector.broadcast(bsp, 'S'.toByte) def part1(bytes: Array[Byte]): Int = var row = 0 var writeIdx = 0 while row < rows do var col = 0 while col < cols do val bv = ByteVector.fromArray(bsp, bytes, row * cols + col, bsp.indexInRange(col, cols)) xa(writeIdx) = jl.Long.reverse(bv.compare(EQ, xv).toLong) ma(writeIdx) = jl.Long.reverse(bv.compare(EQ, mv).toLong) aa(writeIdx) = jl.Long.reverse(bv.compare(EQ, av).toLong) sa(writeIdx) = jl.Long.reverse(bv.compare(EQ, sv).toLong) writeIdx += 1 col += bsp.length row += 1 writeIdx += 1 val bitCols = (rows + 64) / 64 + 1 var count = 0 var bitRow = 0 while bitRow < rows do var bitCol = 0 while bitCol < bitCols - 1 do val idx = bitRow * bitCols + bitCol val forward = xa(idx) & ((ma(idx) << 1) | (ma(idx + 1) >>> 63)) & ((aa(idx) << 2) | (aa(idx + 1) >>> 62)) & ((sa(idx) << 3) | (sa(idx + 1) >>> 61)) count += jl.Long.bitCount(forward) val backward = sa(idx) & ((aa(idx) << 1) | (aa(idx + 1) >>> 63)) & ((ma(idx) << 2) | (ma(idx + 1) >>> 62)) & ((xa(idx) << 3) | (xa(idx + 1) >>> 61)) count += jl.Long.bitCount(backward) inline def row0 = idx inline def row1 = idx + 1 * bitCols inline def row2 = idx + 2 * bitCols inline def row3 = idx + 3 * bitCols val diag1 = xa(row0) & ((ma(row1) << 1) | (ma(row1 + 1) >>> 63)) & ((aa(row2) << 2) | (aa(row2 + 1) >>> 62)) & ((sa(row3) << 3) | (sa(row3 + 1) >>> 61)) count += jl.Long.bitCount(diag1) val diag2 = xa(row3) & ((ma(row2) << 1) | (ma(row2 + 1) >>> 63)) & ((aa(row1) << 2) | (aa(row1 + 1) >>> 62)) & ((sa(row0) << 3) | (sa(row0 + 1) >>> 61)) count += jl.Long.bitCount(diag2) val diag3 = sa(row0) & ((aa(row1) << 1) | (aa(row1 + 1) >>> 63)) & ((ma(row2) << 2) | (ma(row2 + 1) >>> 62)) & ((xa(row3) << 3) | (xa(row3 + 1) >>> 61)) count += jl.Long.bitCount(diag3) val diag4 = sa(row3) & ((aa(row2) << 1) | (aa(row2 + 1) >>> 63)) & ((ma(row1) << 2) | (ma(row1 + 1) >>> 62)) & ((xa(row0) << 3) | (xa(row0 + 1) >>> 61)) count += jl.Long.bitCount(diag4) val down = xa(row0) & ma(row1) & aa(row2) & sa(row3) count += jl.Long.bitCount(down) val up = sa(row0) & aa(row1) & ma(row2) & xa(row3) count += jl.Long.bitCount(up) bitCol += 1 bitRow += 1 count def main(args: Array[String]): Unit = println(part1(bytes))

Dold text
PermalÀnk
Medlem
●
Skrivet av Ingetledigtnamn:

Jag förstÄr vad du vill göra och, ja, det gÄr att göra, men det Àr fel

Koden skulle bli mycket krÄngligare Àn om du bara skrev rak kod som loopade över de Ätta records du fÄr in i ett register. Du mÄste till exempel skriva kod som kör bÄda grenarna av din if else och har k-register som kontrollerar vilka element som instruktionerna pÄverkar. Har du en if else i en if else-gren mÄste du se till att köra bÄda grenar dÀr ocksÄ. Det blir snabbt kombinatoriskt jobbigt. Med en maximal parallellitet pÄ 8 sÄ jag Àr tveksam till om det skulle gÄ snabbare över huvud taget att köra AVX512.

Den stora poÀngen med SIMD Àr ju att kunna operera pÄ mycket data i taget. Ta exempelvis ett filter som rÀknar ut nÄgot baserat pÄ R-, G- och B-vÀrden. DÄ ser man till att ladda R, G och B i olika register och kan köra 64 berÀkningar parallellt. Om inte indata passar i den formen det kom in, dÄ arrangerar man om det till nÄgot som passar bÀttre.

Det gÄr att göra som du föreslÄr men jag tror inte du skulle se den uppsnabbning du hoppas pÄ. Men, men, det skulle vara exotiskt och det var ju dÀr diskussionen startade

Dold text

Jag förstÄr fortfarande inte hur man skulle kunna göra del tvÄ om man inte har en rapport som man sprider ut alla kombinationer av över ett register?
Det finns inga if-else eller nÄgot som blir "kombinatoriskt jobbigt". Det Àr poÀngen med mask register. Jag beskrev antagligen för dÄligt sÄ skrev ner det jag beskrev i kod istÀllet. Det som skiljer mot beskrivningen Àr att jag adderar ihop antalet trÀffar direkt i vektor register istÀllet för att flytta masken till GPR och göra popcount dÀr. Hur parallellt det blir har med lÀngden pÄ varje rapport. Register med bara korta (5 levels) rapporter sÄ arbetar man med 8 * 4 element samtidigt och med bara lÄnga (8 levels) 8 * 7 element. AlltsÄ hamnar man nÄnstans mellan 32 och 56. 500ns / 5 us för del1/del2

Dold text

Dag: 2
SprÄk: Scala (simd edition)

Parsningen Àr inte optimerad utan bara sjÀlva berÀkningarna för del 1 och 2.

object Day02Simd: private val path = """2.txt""" private val data = Using.resource(Source.fromFile(path))(_.getLines.flatMap(_.split(" ").map(_.toByte).padTo(8, 0: Byte)).toArray) private val shuffleArray = (0 to 7).flatMap(idx => Array.tabulate(8)(identity).patch(idx, Nil, 1).padTo(8, 9)).toArray def part1(data: Array[Byte]) = val bsp = ByteVector.SPECIES_512 val lsp = LongVector.SPECIES_512 val m1 = VectorMask.fromLong(bsp, 0x7f7f_7f7f_7f7f_7f7fL) var acc = LongVector.zero(lsp) var idx = 0 while idx < bsp.loopBound(data.length) do val bv = ByteVector.fromArray(bsp, data, idx) val lens = lsp.broadcast(7).sub(bv.reinterpretAsLongs.lanewise(LEADING_ZEROS_COUNT).lanewise(LSHR, 3)) val shifted = bv.slice(1) val m2 = shifted.compare(NE, 0) val m = m1.and(m2) val diff = bv.sub(shifted) val cmp1 = diff.compare(GE, 1, m).and(diff.compare(LE, 3, m)).toVector val cmp2 = diff.compare(GE, -3, m).and(diff.compare(LE, -1, m)).toVector val bits = lens.mul(lsp.broadcast(8)) acc = acc.add(1, cmp1.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) acc = acc.add(1, cmp2.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) idx += bsp.length acc.reduceLanes(ADD).toInt def part2(data: Array[Byte]) = val bsp = ByteVector.SPECIES_512 val lsp = LongVector.SPECIES_512 val m1 = VectorMask.fromLong(bsp, 0x7f7f_7f7f_7f7f_7f7fL) val shuffle = VectorShuffle.fromArray(bsp, shuffleArray, 0) val loadMask = bsp.indexInRange(0, 8) var count = 0 var idx = 0 while idx < bsp.loopBound(data.length) do val bv = ByteVector.fromArray(bsp, data, idx, loadMask) val dist = bv.rearrange(shuffle) val lens = lsp.broadcast(7).sub(dist.reinterpretAsLongs.lanewise(LEADING_ZEROS_COUNT).lanewise(LSHR, 3)) val shifted = dist.slice(1) val m2 = shifted.compare(NE, 0) val m = m1.and(m2) val diff = dist.sub(shifted) val cmp1 = diff.compare(GE, 1, m).and(diff.compare(LE, 3, m)).toVector val cmp2 = diff.compare(GE, -3, m).and(diff.compare(LE, -1, m)).toVector val bits = lens.mul(lsp.broadcast(8)) if cmp1.reinterpretAsLongs .lanewise(BIT_COUNT) .eq(bits) .or(cmp2.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) .anyTrue() then count += 1 idx += 8 count def main(args: Array[String]): Unit = println(s"part1: ${part1(data)}") println(s"part2: ${part2(data)}")

Dold text
SPECIES_PREFERRED -> SPECIES_512 (koden fungerar bara med 512 bitars vektorer)
PermalÀnk
Datavetare ★
●

Dag: 4
SprÄk: Rust

use std::fs; pub const INPUT_FILE: &str = "input.txt"; pub fn read_words(path: &str) -> Vec<Vec<char>> { fs::read_to_string(path) .expect(&format!("File {} should be present", path)) .lines() .map(|s| s.chars().collect()) .collect() } pub fn count_xmas(words: &[Vec<char>]) -> i32 { let character_offsets = &[ vec![(0, 0), (1, 0), (2, 0), (3, 0)], // Vertically vec![(3, 0), (2, 0), (1, 0), (0, 0)], // Rev vertically vec![(0, 0), (0, 1), (0, 2), (0, 3)], // Horizontally vec![(0, 3), (0, 2), (0, 1), (0, 0)], // Rev horizontally vec![(0, 0), (1, 1), (2, 2), (3, 3)], // One diagonal vec![(3, 3), (2, 2), (1, 1), (0, 0)], // Rev One diagonal vec![(0, 3), (1, 2), (2, 1), (3, 0)], // Other diagonal vec![(3, 0), (2, 1), (1, 2), (0, 3)], // Rev Other diagonal ]; let search_term = &['X', 'M', 'A', 'S']; count_word(words, search_term, character_offsets) } pub fn count_x_mas(words: &[Vec<char>]) -> i32 { let character_offsets = &[ vec![(0, 0), (2, 0), (1, 1), (0, 2), (2, 2)], // MAS-MAS vec![(2, 2), (2, 0), (1, 1), (0, 2), (0, 0)], // SAM-MAS vec![(0, 0), (0, 2), (1, 1), (2, 0), (2, 2)], // MAS-SAM vec![(2, 2), (0, 2), (1, 1), (2, 0), (0, 0)], // SAM-SAM ]; let search_term = &['M', 'M', 'A', 'S', 'S']; count_word(words, search_term, character_offsets) } fn count_word( words: &[Vec<char>], search_term: &[char], character_offsets: &[Vec<(usize, usize)>], ) -> i32 { let mut num_xmas = 0; for y in 0..words.len() { for x in 0..words[0].len() { for offsets in character_offsets { if is_match(words, search_term, x, y, offsets) { num_xmas += 1; } } } } num_xmas } fn is_match( words: &[Vec<char>], search_term: &[char], x: usize, y: usize, offsets: &[(usize, usize)], ) -> bool { offsets.iter().zip(search_term).all(|(&(dx, dy), &ch)| { let xi = x + dx; let yi = y + dy; yi < words.len() && xi < words[yi].len() && words[yi][xi] == ch }) } fn main() { let words = read_words(&INPUT_FILE); println!("part 1: {}", count_xmas(&words)); println!("part 2: {}", count_x_mas(&words)); }

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
Datavetare ★
●
Skrivet av jclr:

Jag förstÄr fortfarande inte hur man skulle kunna göra del tvÄ om man inte har en rapport som man sprider ut alla kombinationer av över ett register?
Det finns inga if-else eller nÄgot som blir "kombinatoriskt jobbigt". Det Àr poÀngen med mask register. Jag beskrev antagligen för dÄligt sÄ skrev ner det jag beskrev i kod istÀllet. Det som skiljer mot beskrivningen Àr att jag adderar ihop antalet trÀffar direkt i vektor register istÀllet för att flytta masken till GPR och göra popcount dÀr. Hur parallellt det blir har med lÀngden pÄ varje rapport. Register med bara korta (5 levels) rapporter sÄ arbetar man med 8 * 4 element samtidigt och med bara lÄnga (8 levels) 8 * 7 element. AlltsÄ hamnar man nÄnstans mellan 32 och 56. 500ns / 5 us för del1/del2

Dold text

Dag: 2
SprÄk: Scala (simd edition)

Parsningen Àr inte optimerad utan bara sjÀlva berÀkningarna för del 1 och 2.

object Day02Simd: private val path = """2.txt""" private val data = Using.resource(Source.fromFile(path))(_.getLines.flatMap(_.split(" ").map(_.toByte).padTo(8, 0: Byte)).toArray) private val shuffleArray = (0 to 7).flatMap(idx => Array.tabulate(8)(identity).patch(idx, Nil, 1).padTo(8, 9)).toArray def part1(data: Array[Byte]) = val bsp = ByteVector.SPECIES_512 val lsp = LongVector.SPECIES_512 val m1 = VectorMask.fromLong(bsp, 0x7f7f_7f7f_7f7f_7f7fL) var acc = LongVector.zero(lsp) var idx = 0 while idx < bsp.loopBound(data.length) do val bv = ByteVector.fromArray(bsp, data, idx) val lens = lsp.broadcast(7).sub(bv.reinterpretAsLongs.lanewise(LEADING_ZEROS_COUNT).lanewise(LSHR, 3)) val shifted = bv.slice(1) val m2 = shifted.compare(NE, 0) val m = m1.and(m2) val diff = bv.sub(shifted) val cmp1 = diff.compare(GE, 1, m).and(diff.compare(LE, 3, m)).toVector val cmp2 = diff.compare(GE, -3, m).and(diff.compare(LE, -1, m)).toVector val bits = lens.mul(lsp.broadcast(8)) acc = acc.add(1, cmp1.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) acc = acc.add(1, cmp2.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) idx += bsp.length acc.reduceLanes(ADD).toInt def part2(data: Array[Byte]) = val bsp = ByteVector.SPECIES_512 val lsp = LongVector.SPECIES_512 val m1 = VectorMask.fromLong(bsp, 0x7f7f_7f7f_7f7f_7f7fL) val shuffle = VectorShuffle.fromArray(bsp, shuffleArray, 0) val loadMask = bsp.indexInRange(0, 8) var count = 0 var idx = 0 while idx < bsp.loopBound(data.length) do val bv = ByteVector.fromArray(bsp, data, idx, loadMask) val dist = bv.rearrange(shuffle) val lens = lsp.broadcast(7).sub(dist.reinterpretAsLongs.lanewise(LEADING_ZEROS_COUNT).lanewise(LSHR, 3)) val shifted = dist.slice(1) val m2 = shifted.compare(NE, 0) val m = m1.and(m2) val diff = dist.sub(shifted) val cmp1 = diff.compare(GE, 1, m).and(diff.compare(LE, 3, m)).toVector val cmp2 = diff.compare(GE, -3, m).and(diff.compare(LE, -1, m)).toVector val bits = lens.mul(lsp.broadcast(8)) if cmp1.reinterpretAsLongs .lanewise(BIT_COUNT) .eq(bits) .or(cmp2.reinterpretAsLongs.lanewise(BIT_COUNT).eq(bits)) .anyTrue() then count += 1 idx += 8 count def main(args: Array[String]): Unit = println(s"part1: ${part1(data)}") println(s"part2: ${part2(data)}")

Dold text

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
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: 4
SprÄk: Go
Lösning:

package main import ( "bufio" "fmt" "log" "os" ) func readValuesFromFile(filename string) ([][]rune, error) { file, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("failed to open file: %w", err) } defer file.Close() scanner := bufio.NewScanner(file) values := [][]rune{} for scanner.Scan() { row := []rune(scanner.Text()) values = append(values, row) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error reading file: %w", err) } return values, nil } type Direction struct { X int Y int } func getAvailableDirections(x int, y int, length int, wordSearch [][]rune) []Direction { directions := []Direction{{X: 0, Y: 1}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: -1}, {X: -1, Y: 0}, {X: -1, Y: -1}, {X: 1, Y: -1}, {X: -1, Y: 1}} available := []Direction{} for _, direction := range directions { newX := x + length*direction.X newY := y + length*direction.Y if newX >= 0 && newX < len(wordSearch[0]) && newY >= 0 && newY < len(wordSearch) { available = append(available, direction) } } return available } func wordCount(wordSearch [][]rune, word []rune) (int, error) { count := 0 for y, row := range wordSearch { for x, col := range row { if col == word[0] { availableDirections := getAvailableDirections(x, y, len(word)-1, wordSearch) directionLoop: for _, direction := range availableDirections { for i, char := range word[1:] { newX := x + (i+1)*direction.X newY := y + (i+1)*direction.Y if wordSearch[newY][newX] != char { continue directionLoop } } count++ } } } } return count, nil } func getCharacters(wordSearch [][]rune, x int, y int, direction Direction, length int) []rune { result := []rune{} for i := range length { newX := x + (i+1)*direction.X newY := y + (i+1)*direction.Y result = append(result, wordSearch[newY][newX]) } return result } func xwordCount(wordSearch [][]rune, word []rune) (int, error) { if len(word)%2 == 0 { return 0, fmt.Errorf("xword length must be odd: %s", string(word)) } diagionals := [][]Direction{{{X: 1, Y: 1}, {X: -1, Y: -1}}, {{X: 1, Y: -1}, {X: -1, Y: 1}}} lengthFromMiddle := (len(word) - 1) / 2 reverseWord := reverseRunes(word) count := 0 for y, row := range wordSearch { if y < lengthFromMiddle || y > len(wordSearch)-1-lengthFromMiddle { continue } outer: for x, col := range row { if x < lengthFromMiddle || x > len(wordSearch[0])-1-lengthFromMiddle { continue } if col == word[lengthFromMiddle] { for _, directions := range diagionals { diag1 := getCharacters(wordSearch, x, y, directions[0], lengthFromMiddle) diag2 := getCharacters(wordSearch, x, y, directions[1], lengthFromMiddle) if !(compareRuneSlices(diag1, word[:lengthFromMiddle]) && compareRuneSlices(diag2, word[lengthFromMiddle+1:])) && !(compareRuneSlices(diag1, reverseWord[:lengthFromMiddle]) && compareRuneSlices(diag2, reverseWord[lengthFromMiddle+1:])) { continue outer } } count++ } } } return count, nil } func compareRuneSlices(slice1, slice2 []rune) bool { if len(slice1) != len(slice2) { return false } for i := range slice1 { if slice1[i] != slice2[i] { return false } } return true } func reverseRunes(runes []rune) []rune { runesCopy := make([]rune, len(runes)) copy(runesCopy, runes) n := len(runesCopy) for i := 0; i < n/2; i++ { runesCopy[i], runesCopy[n-1-i] = runesCopy[n-1-i], runesCopy[i] } return runesCopy } func main() { value, err := readValuesFromFile("input.txt") if err != nil { log.Fatalf("Error reading data: %s", err) } total, err := wordCount(value, []rune("XMAS")) if err != nil { log.Fatalf("Error calculating part 1: %s", err) } fmt.Printf("Part 1: %d\n", total) total2, err := xwordCount(value, []rune("MAS")) if err != nil { log.Fatalf("Error calculating part 2: %s", err) } fmt.Printf("Part 2: %d\n", total2) }

Dold text

Hade verkligen behövt stÀdas upp, men fÄr duga sÄdÀr idag...

Visa signatur

Desktop spel m.m.: Ryzen 9800X3D || MSI X870 Tomahawk Wifi || MSI Ventus 3x 5080 || Gskill FlareX 6000 64GB || Kingston KC3000 2TB || Samsung 970 EVO Plus 2TB || Samsung 960 Pro 1TB || Fractal Torrent || Asus PG42UQ 4K OLED
Arbetsstation: Ryzen 7945HX || Minisforum BD790i || Asus Proart 4070 Ti Super || Kingston Fury Impact 5600 65 GB || WD SN850 2TB || Samsung 990 Pro 2TB || Fractal Ridge
Proxmox server: Ryzen 5900X || Asrock Rack X570D4I-2T || Kingston 64GB ECC || WD Red SN700 1TB || Blandning av WD Red / Seagate Ironwolf för lagring || Fractal Node 304

PermalÀnk
Medlem ★
●

Dag 5 Del 1 ENDAST !!!
PHP - Körs via "Code Runner" i VSCode Terminal

5.php - Dag 5 Del 1 Endast (fÄ kommentarer)

<?php // Data from Source: https://adventofcode.com/2024/day/5/input $data1 = "65|79 ETC|ETC"; $data2 = "47,89,77,74,61,53,82,38,45,73,15,76,41,24,56,55,67,68,95 ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC,ETC"; // Get data in managable way $data1 = array_map('rtrim', explode("\n", $data1)); $data2 = str_replace(",", "|", $data2); $data2 = array_map('rtrim', explode("\n", $data2)); $data1 = array_map(function ($x) { return explode("|", $x); }, $data1); function ExtractMiddleArrayValue($array) { return $array[ceil(count($array) / 2) - 1]; } function TwoArrayElementsAreInStringAndInValidOrder($string, $array) { $first = $array[0]; $second = $array[1]; if (strpos($string, $first) !== false && strpos($string, $second) !== false) { if (strpos($string, $first) < strpos($string, $second)) { return true; } else { return false; } } return null; } function getValidStrings($testdata1, $testdata2) { foreach ($testdata2 as $string) { $valid = true; foreach ($testdata1 as $rule) { $result = TwoArrayElementsAreInStringAndInValidOrder($string, $rule); if ($result === false) { $valid = false; break; } } if ($valid) { $validStrings[] = $string; } } return $validStrings; } // Get only valid strings and sum up their middle array element value $validStrings = getValidStrings($data1, $data2); $sumOfMiddleNumbers = 0; foreach ($validStrings as $string) { $sumOfMiddleNumbers += ExtractMiddleArrayValue(explode("|", $string)); } echo "Day 5 Part 1 Answer: " . $sumOfMiddleNumbers; ?>

Dold text

Jag var orolig redan i Dag 5 Del 1 att det jag skulle ha missat nÄgot specialfall som skulle leda till ett fel redan pÄ första försöket. Men jag provade algoritmen först med de testdata frÄn pusslet för att se om jag dÄ skulle fÄ samma svar som enligt pusslets exempel vilket jag fick. SÄ dÄ litade jag pÄ att det skulle fungera. Och det gjorde det, för Dag 5 Del 1 iaf!

Nu till Dag 5 Del 2...😂

Mvh,
WKF.

Visa signatur

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

PermalÀnk
Medlem ★
●

PHP via "Code Runner" i VSCode Terminal
Dag 5 Del 2 - "PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in C:\xampp\htdocs\aoc2024\5.php on line 1469"

Jag fÄr ej delta lÀngre i denna del för denna dag för jag fÄr slut pÄ minne (jag provade Àven med init_set('memory_limit', '8G');) med de riktiga data medan testdata ger exakt samma svar som enligt pusselexemplet sÄ jag fÄr aldrig nÄgot svar som kanske kan ge mig ett svar att skicka in för Dag 5 Del 2!

// FRÅGA: Jag undrar om jag skulle kunna fixa permutationsfunktionen // för faktorialfunktionen Ă€r ju till för att berĂ€tta hur mĂ„nga olika // permutationer en given array har. Och sĂ„ ska den loopa igenom // sĂ„ mĂ„nga gĂ„nger för att prova alla kombinationer. function findFactorialNumberForArray($n) { if (!is_array($n)) { throw new Exception("Input must be an array"); } else { $n = count($n); } $factorial = 1; for ($i = 1; $i <= $n; $i++) { $factorial *= $i; } return $factorial; } // Function that generates permutations of an array to brute force all possible combinations function generatePermutations($array) { if (count($array) === 1) { return [$array]; } $permutations = []; foreach ($array as $key => $value) { // Remove the current element $remaining = $array; unset($remaining[$key]); // Recursively generate permutations for the rest $remainingPermutations = generatePermutations($remaining); foreach ($remainingPermutations as $permutation) { // Prepend the current element to each permutation $permutations[] = array_merge([$value], $permutation); } } return $permutations; } function getInvalidStrings($testdata1, $testdata2) { $invalidStrings = []; foreach ($testdata2 as $string) { foreach ($testdata1 as $rule) { $result = TwoArrayElementsAreInStringAndInValidOrder($string, $rule); if ($result === false) { $invalidStrings[] = $string; break; } } } return $invalidStrings; } // RUN DAY 5 PART 2 $invalidStrings = getInvalidStrings($testdata1, $testdata2); // Get all invalid strings instead //var_dump($invalidStrings); // We loop through and generate permutations (combinations) of each invalid string // and for each permutations we check against valid rules and if they are valid foreach ($invalidStrings as $string) { $string = explode("|", $string); $permutations = generatePermutationsIteratively($string); foreach ($permutations as $permutation) { $valid = true; foreach ($testdata1 as $rule) { $result = TwoArrayElementsAreInStringAndInValidOrder(implode("|", $permutation), $rule); if ($result === false) { $valid = false; break; } } if ($valid) { $sumOfMiddleNumbers += ExtractMiddleArrayValue($permutation); $foundValid = true; break; } } } echo "\nDay 5 Part 2 Answers: " . $sumOfMiddleNumbers; // KRUXET Ă€r ju att Ă€ven om jag hittar en giltig regel sĂ„ kan ju nĂ„gon annan regel // Ă€ndĂ„ vara ogiltig vilket skulle innebĂ€ra att strĂ€ngen skulle ogiltighetsförklaras. HM!

Dold text

Hur vanligt tidigt Àr det att pusslen börjar "kvÀva" pÄ kreativiteten av lösningarna och börjar tvinga en att Äteruppfinna diverse kÀnda prestandaalgoritmer (t.ex. Dijkstras) för att kunna lösa dagspussel och att bruteforce-lösningar ej fungerar lÀngre pga. minnesbrist?

EDIT:

Jag gjorde det fruktansvÀrt oetiska och frÄgade chatGPT4o1-gratis om varför inte min lösning fungerarde pga. minnesbrist. Den nÀmnde nÄgot dÄ om "Topological sort" och "Kahns Algoritm" vilket jag aldrig har hört talas om gÀllande sorteringsmetoder. Hur vanlig Àr den i arbetslivet för Webbutvecklare att den Àr vÀrd att nöta ner sig i?

Dold text

Mvh,
WKF.

Visa signatur

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

PermalÀnk
Medlem ★
●

Dag: 5
SprÄk: Python

from collections import defaultdict from functools import cmp_to_key (order, pages), precede, s = open("input05.txt", "r").read().split("\n\n"), defaultdict(set), [0,0] [precede[o.split("|")[0]].add(o.split("|")[1]) for o in order.split("\n")] for p in pages.strip().split("\n"): l1 = p.split(",") l2 = sorted(l1, key=cmp_to_key(lambda x, y: 1 if x in precede[y] else -1)) s[l1 != l2] += int(l2[len(l2) // 2]) print(*s)

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

Jag gjorde det fruktansvÀrt oetiska och frÄgade chatGPT4o1-gratis om varför inte min lösning fungerarde pga. minnesbrist. Den nÀmnde nÄgot dÄ om "Topological sort" och "Kahns Algoritm" vilket jag aldrig har hört talas om gÀllande sorteringsmetoder. Hur vanlig Àr den i arbetslivet för Webbutvecklare att den Àr vÀrd att nöta ner sig i?

Dold text

Vi kan vÀl nöja oss med att konstatera att det finns bÀttre sÀtt att sortera en lista Àn att generera alla möjliga permutationer för att vÀlja den som Àr sorterad. Du kanske kan hitta en effektivare algoritm snarare Àn att öka pÄ minnesstorleken. Topological sort lÄter som en bra ide

Stavfel
PermalÀnk
Medlem ★
●
Skrivet av Ingetledigtnamn:

Vi kan vÀl nöja oss med att konstatera att det finns bÀttre sÀtt att sortera en lista Àn att generera alla möjliga permutationer för att vÀlja den som Àr sorterad. Du kanske kan hitta en effektivare algoritm snarare Àn att öka pÄ minnesstorleken. Topological sort lÄter som en bra ide

AngÄende Dag 5 Del 2:

Ja, inte ens att skicka in bara ett array element i taget fungerade. ChunkSize 1 fungerar inte ens (en strÀng i taget) utan den kraschar pÄ kodraden: "$array[$i] = $array[$start_i];" i permutationsfunktionen.

// Function that generates permutations of an array to brute force all possible combinations function generatePermutations($array) { $result = []; $recurse = function ($array, $start_i = 0) use (&$result, &$recurse) { if ($start_i === count($array) - 1) { array_push($result, $array); } for ($i = $start_i; $i < count($array); $i++) { //Swap array value at $i and $start_i $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; //Recurse $recurse($array, $start_i + 1); //Restore old order $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; } }; $recurse($array); return $result; } function solvePart2($invalidStrings, $testdata1) { $chunkSum = 0; foreach ($invalidStrings as $string) { $string = explode("|", $string); $permutations = generatePermutations($string); foreach ($permutations as $permutation) { $valid = true; foreach ($testdata1 as $rule) { $result = TwoArrayElementsAreInStringAndInValidOrder(implode("|", $permutation), $rule); if ($result === false) { $valid = false; break; } } if ($valid) { $chunkSum += ExtractMiddleArrayValue($permutation); $foundValid = true; break; } } } return $chunkSum; } $chunks = array_chunk($invalidStrings, 1); foreach ($chunks as $chunk) { $sumOfMiddleNumbers += solvePart2($chunk, $testdata1); unset($chunk); gc_collect_cycles(); }

Dold text

Jag undrar hur vanlig den sorteringsalgoritmen (verkar komma frÄn Grafteori) Àr i vardagslivet för utvecklare? Kanske databaser anvÀnder sig av sÄdana prestandaoptimerade sorteringsalgoritmer i vissa fall?

Och sĂ„ kĂ€nner jag mig smĂ„tt uppgiven hur din lösning Ă€r sĂ„ kort och Ă€ndĂ„ sĂ„ rĂ€tt medan min Ă€r sĂ„ lĂ„ng och samtidigt sĂ„ himla trasig eftersom den inte gĂ„r att köra?! đŸ„ČđŸ€Ł

Mvh,
WKF.

Visa signatur

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

PermalÀnk
Medlem
●

Dag: 5
SprÄk: Scala

val input = Using.resource(Source.fromFile("5.txt"))(_.mkString) val (dependencies, updates) = input.split("\n\n").pipe: case Array(fst, snd) => fst.split('\n').map { case s"$x|$y" => (x.toInt, y.toInt) }.groupMap(_._2)(_._1) -> snd.split('\n').map(_.split(',').map(_.toInt)) extension (xs: Array[Int]) def isSortedBy(p: (Int, Int) => Boolean): Boolean = var idx = 0 while idx < xs.length - 1 do if !p(xs(idx), xs(idx + 1)) then return false idx += 1 true def middle: Int = xs(xs.length / 2) val ordering = (x: Int, y: Int) => dependencies(y).contains(x) // del 1 updates.filter(_.isSortedBy(ordering)).map(_.middle).sum // del 2 updates.filterNot(_.isSortedBy(ordering)).map(_.sortWith(ordering)).map(_.middle).sum

Dold text
PermalÀnk
Medlem
●

Parsing (+ tvivla pÄ sig sjÀlv) fÄr det verkligen att ta lÀngst tid ibland.

Dag: 5
SprÄk: Java
GitHub

PermalÀnk
Medlem ★
●
Skrivet av WebbkodsFrilansaren:

Jag undrar hur vanlig den sorteringsalgoritmen (verkar komma frÄn Grafteori) Àr i vardagslivet för utvecklare? Kanske databaser anvÀnder sig av sÄdana prestandaoptimerade sorteringsalgoritmer i vissa fall?

Och sĂ„ kĂ€nner jag mig smĂ„tt uppgiven hur din lösning Ă€r sĂ„ kort och Ă€ndĂ„ sĂ„ rĂ€tt medan min Ă€r sĂ„ lĂ„ng och samtidigt sĂ„ himla trasig eftersom den inte gĂ„r att köra?! đŸ„ČđŸ€Ł

Mvh,
WKF.

Det "topologiska" handlar om att A | B-reglerna beskriver en graf över hur vÀnsterleden beror pÄ högerleden, men vi kan strunta i det. TÀnk dig en sorteringsfunktion som, istÀllet för att jÀmföra de numeriska vÀrdena pÄ A och B, slÄr upp i reglerna om A Àr mindre Àn B (dvs A skall komma före B). Det hÀnder relativt ofta att man vill sortera pÄ andra saker Àn rena numeriska vÀrden eller strÀngar. Du kan rÀkna med att du behöver göra det under flera av de kommande uppgifterna i AoC

JÀttespoiler, lÀs inte denna om du inte redan har klarat uppgiften eller gett upp!

Tricket till min korta lösning Àr att sorteringsalgortimen och unika vÀrden i listorna gör att sorteringen Àr stabil, dvs att om vi sorterar en redan sorterad lista kommer resultatet bli exakt samma som input.

lists = alla listor
s1 = alla ordande listor i lists
s2 = alla oordnade listor i lists

I del 1 skall vi summera mittelementen i s1.
I del 2 skall vi summera mittelementen i sorted(s2).

Men nÀr en sorterad lista fortfarande Àr sorterad efter att vi sorterar den igen sÄ kan vi göra samma sak i bÄda fallen.

I del 1 skall vi summera mittelementen i sorted(s1).
I del 2 skall vi summera mittelementen i sorted(s2).

Nu kan vi iterera över lists (utan att dela upp i ordnade och oordnade), jÀmföra en lista (l1) med den den sorterade versionen av samma lista (l2=sorted(l1)). l1 == l2 addera till summan för del1, l1 !=l2 addera till summan för del 2.

Dold text
Stavfel
PermalÀnk
Medlem ★
●

Dag: 5
SprÄk: C#
Dag 5

RÀtt nöjd med dagens lösning.

Utmaningen Àr vÀl att inte slÄ upp google vid minsta hinder... I Är Àr det endast egen tankekraft som gÀller

public class Solver { private static bool IsValidLine(Dictionary<string, List<string>> rulesInput, string line) { var pageNumbers = line.Split(',').ToList(); for (var i = 0; i < pageNumbers.Count; i++) { var pageNumber = pageNumbers[i]; if (!rulesInput.TryGetValue(pageNumber, out var ruleValues)) continue; if (ruleValues.Any(mustBeLessThan => pageNumbers.Where((t, j) => t == mustBeLessThan && j <= i).Any())) { return false; } } return true; } private static string MakeLineValid(Dictionary<string, List<string>> rulesInput, string line) { var newLine = line; do { var pageNumbers = newLine.Split(',').ToList(); for (var i = 0; i < pageNumbers.Count; i++) { var pageNumber = pageNumbers[i]; if (!rulesInput.TryGetValue(pageNumber, out var ruleValues)) continue; foreach (var mustBeLessThan in ruleValues) { for (var j = 0; j < pageNumbers.Count; j++) { if (pageNumbers[j] != mustBeLessThan) continue; if (j > i) continue; var temp = pageNumbers[i].ToString(); pageNumbers[i] = pageNumbers[j]; pageNumbers[j] = temp; } } } newLine = string.Join(",", pageNumbers); } while (!IsValidLine(rulesInput, newLine)); return newLine; } private static Dictionary<string, List<string>> ParseInputRules(List<string> inputRules) { inputRules = inputRules.OrderBy(i => i.Split('|')[0]).ThenBy(i => i.Split('|')[1]).ToList(); var rulesDict = new Dictionary<string, List<string>>(); foreach (var rule in inputRules) { var key = rule.Split('|')[0]; var value = rule.Split('|')[1]; if(rulesDict.ContainsKey(key)) rulesDict[key].Add(value); else rulesDict.Add(key, [value]); } return rulesDict; } public static int Run_PartOne(string input) { var split = input.Split("\r\n\r\n"); var rules = split[0].Split("\r\n").ToList(); var updates = split[1].Split("\r\n").ToList(); var rulesDict = ParseInputRules(rules); return updates .Where(line => IsValidLine(rulesDict, line)) .Select(line => line.Split(',').ToList()) .Select(numbers => int.Parse(numbers[(numbers.Count - 1) / 2])) .Sum(); } public static int Run_PartTwo(string input) { var split = input.Split("\r\n\r\n"); var rules = split[0].Split("\r\n").ToList(); var updates = split[1].Split("\r\n").ToList(); var rulesDict = ParseInputRules(rules); return updates .Where(line => !IsValidLine(rulesDict, line)) .Select(line => MakeLineValid(rulesDict, line).Split(',').ToList()) .Select(numbers => int.Parse(numbers[(numbers.Count - 1) / 2])) .Sum(); } }

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 Ingetledigtnamn:

Det "topologiska" handlar om att A | B-reglerna beskriver en graf Àver hur vÀsterleden beror pÄ högerleden, men vi kan strunta i det. TÀnk dig en sorteringsfunktion som, istÀllet för att jÀmföra de numeriska vÀrdena pÄ A och B, slÄr upp i reglerna om A Àr mindre Àn B (dvs A skall komma före B). Det hÀnder relativt ofta att man vill sortera pÄ andra saker Àn rena numeriska vÀrden eller strÀngar. Du kan rÀkna med att du behöver göra det under flera av de kommande uppgifterna i AoC

JÀttespoiler, lÀs inte denna om du inte redan har klarat uppgiften eller gett upp!

Tricket till min korta lösning Àr att sorteringsalgortimen och unika vÀrden i listorna gör att sorteringen Àr stabil, dvs att om vi sorterar en redan sorterad lista kommer resultatet bli exakt samma som input.

lists = alla listor
s1 = alla ordande listor i lists
s2 = alla oordnade listor i lists

I del 1 skall vi summera mittelementen i s1.
I del 2 skall vi summera mittelementen i sorted(s2).

Men nÀr en sorterad lista fortfarande Àr sorterad efter att vi sorterar den igen sÄ kan vi göra samma sak i bÄda fallen.

I del 1 skall vi summera mittelementen i sorted(s1).
I del 2 skall vi summera mittelementen i sorted(s2).

Nu kan vi iterera över lists (utan att dela upp dem in ordnade och oordnade), jÀmföra en lista (l1) med den den sorterade versionen av samma lista (l2=sorted(l1)). l1 == l2 addera till summan för del1, l1 !=l2 addera till summan för del 2.

Dold text

Tack sÄ mycket för beskrivningen!

Jag valde att kasta in handduken för denna gĂ„ng för min egen psykiska hĂ€lsas skull för i Ă„r i AoC 2024. Jag fick en blandad klump i magen av ilska och förtvivlan över mig sjĂ€lv som tog ett tag att gĂ„ över och det Ă€r ju helt klart fel slags kĂ€nslor att behöva associera med programmering!đŸ€Ł

Jag kom Ă„tminstone lĂ€ngre i Ă„r Ă€n förra Ă„ret. NĂ€sta Ă„r har jag förhoppningsvis nĂ„gon faktisk arbetslivserfarenhet inom algoritmer att kunna applicera. Jag misstĂ€nker att Leetcooders har ett stort övertag hĂ€r och möjligen Ă€r det sĂ„dana slags personer som bidrar till AoC-kalendrarnas dagliga kodutmaningar?đŸ€”

GLHF till alla övriga med helt klart starkare "kodpsyke" Ă€n jag!đŸ«Ą

Mvh,
WKF.

Visa signatur

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

PermalÀnk
Medlem ★
●

Dag: 5
SprÄk: C#

GÄrdagens lÀgger jag inte upp, den Àr lika hemsk som mÄnga andras..

För dagens kÀnde jag pÄ mig att det skulle sorteras i del tvÄ som mÄnga andra Är sÄ lika bra att implementera en IComparer direkt. Det lönade ju sig

public class Puzzle_2024_5 : Puzzle { public override object PartOne(string data) { var (rules, updates) = LoadData(data); RuleComparer comparer = new RuleComparer(rules); return updates.Aggregate(0, (acc, update) => { var sorted = update.OrderBy(x => x, comparer); if (update.SequenceEqual(sorted)) acc += update[update.Count / 2]; return acc; }); } public override object PartTwo(string data) { var (rules, updates) = LoadData(data); RuleComparer comparer = new RuleComparer(rules); return updates.Aggregate(0, (acc, update) => { var sorted = update.OrderBy(x => x, comparer).ToList(); if (!update.SequenceEqual(sorted)) { acc += sorted[sorted.Count / 2]; } return acc; }); } private static (HashSet<(int, int)> rules, List<List<int>> updates) LoadData(string data) { var sections = data.Split("\r\n\r\n"); var rulesList = sections[0].Split("\r\n").Select(line => line.Split('|').Select(int.Parse).ToList()).ToList(); HashSet<(int, int)> rules = []; foreach (var rule in rulesList) { rules.Add((rule[0], rule[1])); } var updates = sections[1].Split("\r\n").Select(line => line.Split(',').Select(int.Parse).ToList()).ToList(); return (rules, updates); } private class RuleComparer(HashSet<(int i, int j)> rules) : IComparer<int> { public int Compare(int x, int y) { if (rules.Contains((x, y))) return -1; return rules.Contains((y, x)) ? 1 : 0; } } }

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

Vad roligt att det Àr AoC igen!

Jag gick med som "anonymous user #1176461", men kommer inte vara uppe i ottan och kÀmpa.

I Är ska jag köra C++ för att kÀnna pÄ det hÀr nya ranges som Àr en förenklad syntax för algoritmer och iteratorer och sÄnt.

Dag: 1
SprÄk: C++

#include <algorithm> // count, fold_left, sort #include <cmath> // std::abs #include <fstream> #include <functional> // std::plus #include <iostream> #include <ranges> // zip_transform #include <vector> int main(int argc, char* argv[]) { // Reading the input std::vector<int> left {}; std::vector<int> right {}; std::ifstream f(argv[1], std::ios::in); int first_num, second_num; while (f >> first_num && f >> second_num) { left.push_back(first_num); right.push_back(second_num); } std::ranges::sort(left); std::ranges::sort(right); // Part 1 auto distance = [](auto a, auto b) { return std::abs(a - b); }; auto part1 = std::ranges::fold_left( std::views::zip_transform(distance, right, left), 0, std::plus<>() ); // Part 2 auto similarity = [right](auto acc, auto b) { return std::ranges::count(right, b) * b + acc; }; auto part2 = std::ranges::fold_left(left, 0, similarity); std::cout << "Part 1: " << part1 << "\nPart 2: " << part2 << '\n'; return 0; }

Dold text

std::views::zip_transform

cool!

Visa signatur

Sweclockers lurker nÀr min plÄnbok behöver Äsikter.

PermalÀnk
Medlem ★
●

Dag: 5
SprÄk: F#

open System let pairs, updates = let data = System.IO.File.ReadAllText("input.txt") |> _.Split("\n\n", System.StringSplitOptions.RemoveEmptyEntries) let pairs = data.[0] |> _.Split("\n") |> Seq.map (fun x -> x.Split("|") |> (fun x-> (int x.[0], int x.[1]))) |> Set.ofSeq let updates = data.[1] |> _.Split("\n") |> Seq.map (fun x -> x.Split(",", StringSplitOptions.TrimEntries) |> Seq.map int |> Seq.toList) |> Seq.toList (pairs, updates) let isSorted = List.pairwise >> List.exists (fun (x,y) -> Set.contains (y,x) pairs) >> not let part' filter mapper = updates |> List.filter filter |> List.map mapper |> Seq.map (fun x-> let length = x |> Seq.length let middle = length / 2 x |> Seq.skip middle |> Seq.head ) |> Seq.sum let comparer x y = if Set.contains (x,y) pairs then -1 elif Set.contains (y,x) pairs then 1 else 0 let part1 = part' isSorted id let part2 = part' (isSorted >> not) (List.sortWith comparer) printfn "part 1: %A" part1 printfn "part 2: %A" part2

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 ★
●

Dag: 5
SprÄk: Rust

Den Àr lite "varför inte?"... Gjorde en bitmask för varje tal dÀr masken pÄ index N sÀger vilka andra sidor sida N fÄr vara före.
Lite poÀnglöst pÄ det stora hela, tar ca x10 lÀngre att lÀsa in data Àn göra berÀkningen. I.e. bra exempel pÄ att optimera fel sak

use std::fs; pub const INPUT_FILE: &str = "input.txt"; pub fn read_page_rules(path: &str) -> (Vec<u128>, Vec<Vec<u32>>) { let file_content = fs::read_to_string(path).expect(&format!("File {} should be present", path)); let mut groups = file_content.split("\n\n"); ( groups .next() .expect("Missing first group") .lines() .fold(Vec::new(), |mut filter, line| { let mut parts = line.split('|'); let a: usize = parts.next().unwrap().parse().unwrap(); let b: usize = parts.next().unwrap().parse().unwrap(); if a >= filter.len() { filter.resize(a + 1, 0); } filter[a] |= 1 << b; filter }), groups .next() .expect("Missing second group") .lines() .map(|line| line.split(',').map(|num| num.parse().unwrap()).collect()) .collect(), ) } pub fn correct_order_middle_page_sum(rules: &[u128], page_updates: &[Vec<u32>]) -> u32 { page_updates .iter() .filter(|updates| is_pages_in_correct_order(rules, updates)) .map(|pages| pages[pages.len() / 2]) .sum() } pub fn corrected_order_middle_page_sum(rules: &[u128], page_updates: &[Vec<u32>]) -> u32 { page_updates .iter() .filter(|updates| !is_pages_in_correct_order(rules, updates)) .map(|incorrect_order| correct_order(rules, incorrect_order)) .map(|pages| pages[pages.len() / 2]) .sum() } fn is_pages_in_correct_order(rules: &[u128], page_updates: &[u32]) -> bool { let mut mask = 1 << page_updates[0]; for i in 1..page_updates.len() { let page = page_updates[i] as usize; if (mask & rules[page]) != 0 { return false; } mask |= 1 << page; } true } fn correct_order(rules: &[u128], incorrect_order: &[u32]) -> Vec<u32> { let mut pages = incorrect_order.to_vec(); let mut mask: u128 = pages.iter().map(|&page| 1 << page).sum(); for i in (1..pages.len()).rev() { for (j, &page) in pages[0..=i].iter().enumerate() { if (mask & rules[page as usize]) == 0 { pages.swap(i, j); mask ^= 1 << page; break; } } } pages } fn main() { let (rules, page_updates) = read_page_rules(&INPUT_FILE); println!( "part 1: {}", correct_order_middle_page_sum(&rules, &page_updates) ); println!( "part 2: {}", corrected_order_middle_page_sum(&rules, &page_updates) ); }

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: 3
SprÄk: C

#include <stdio.h> #include <stdlib.h> #include <ctype.h> int main(int argc, char *argv[]) { FILE *fp; char *a = NULL; long flen; fp = fopen("input.txt", "r"); (void)fseek(fp, 0L, SEEK_END); flen = ftell(fp); rewind(fp); a = malloc((size_t)flen); fread(a, 1, (size_t)flen, fp); fclose(fp); // part 1 and 2 int enabled = 1, j = 0, k = 0, d1 = 0, d2 = 0, part1 = 0, part2 = 0; for (int i = 0; i < (flen-8); i++) { // mul(1,1) minimum 8 chars j = k = d1 = d2 = 0; if (a[i] == 'd' && a[i+1] == 'o' && a[i+2] == '(' && a[i+3] == ')') { enabled = 1; i += 3; continue; } else if (a[i] == 'd' && a[i+1] == 'o' && a[i+2] == 'n' && a[i+3] == '\'' && a[i+4] == 't' && a[i+5] == '(' && a[i+6] == ')') { enabled = 0; i += 6; continue; } if (a[i] == 'm' && a[i+1] == 'u' && a[i+2] == 'l' && a[i+3] == '(') { for(j = i+4; j < i+4+4; j++) { if (isdigit(a[j])) { d1 = d1 * 10 + (a[j]-'0'); continue; } else if (a[j] == ',') { for (k = j+1; k < (k+1+3) && k < flen; k++) { if (isdigit(a[k])) { d2 = d2 * 10 + (a[k]-'0'); continue; } else if (a[k] == ')') { part1 += d1 * d2; if (enabled) { part2 += d1 * d2; } } i = k-1; break; } if (i == k-1) { break; } } else { i = j-1; break; } } } } free(a); printf("part 1: %d\n", part1); printf("part 2: %d\n", part2); return 0; }

Dold text

Bara för att visa att det relativt enkelt gÄr att lösa utan regex

Visa signatur

Citera mig för svar.
Arch Linux

PermalÀnk
Medlem ★
●

Dag: 5
SprÄk: Go
Lösning:

package main import ( "bufio" "fmt" "log" "os" "sort" "strconv" "strings" ) func readRulesFromFile(filename string) ([]Rule, error) { file, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("failed to open file: %w", err) } defer file.Close() scanner := bufio.NewScanner(file) values := []Rule{} for scanner.Scan() { columns := strings.Split(scanner.Text(), "|") before, err := strconv.Atoi(columns[0]) if err != nil { return nil, fmt.Errorf("rule data not an integer: %s", columns[0]) } after, err := strconv.Atoi(columns[1]) if err != nil { return nil, fmt.Errorf("rule data not an integer: %s", columns[1]) } values = append(values, Rule{Before: before, After: after}) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error reading file: %w", err) } return values, nil } func readValuesFromFile(filename string) ([][]int, error) { file, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("failed to open file: %w", err) } defer file.Close() scanner := bufio.NewScanner(file) values := [][]int{} for scanner.Scan() { columns := strings.Split(scanner.Text(), ",") update := []int{} for _, val := range columns { value, err := strconv.Atoi(val) if err != nil { return nil, fmt.Errorf("input data not an integer: %s", val) } update = append(update, value) } values = append(values, update) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error reading file: %w", err) } return values, nil } type Rule struct { Before int After int } type CustomSorter struct { data []int compareFunc func(a, b int) bool } func (cs CustomSorter) Len() int { return len(cs.data) } func (cs CustomSorter) Less(i, j int) bool { return cs.compareFunc(cs.data[i], cs.data[j]) } func (cs CustomSorter) Swap(i, j int) { cs.data[i], cs.data[j] = cs.data[j], cs.data[i] } func isValidUpdate(update []int, compareFunc func(a, b int) bool) bool { sorter := CustomSorter{ data: update, compareFunc: compareFunc, } return sort.IsSorted(sorter) /* did this first, but there's really no point after having the sorter... for i := range len(update) { validPreviousPages := pageToValidPreviousPages[update[i]] for _, previousPage := range update[:i] { if !contains(validPreviousPages, previousPage) { return false } } } return true */ } func isInvalidUpdate(update []int, compareFunc func(a, b int) bool) bool { return !isValidUpdate(update, compareFunc) } func getCompareFunc(rules []Rule) func(a, b int) bool { pageToValidPreviousPages := getRulesMap(rules) compareFunc := func(a, b int) bool { validPrevious := pageToValidPreviousPages[b] return contains(validPrevious, a) } return compareFunc } func filterUpdates(updates [][]int, rules []Rule, filterFunc func([]int, func(int, int) bool) bool) [][]int { valid := [][]int{} compareFunc := getCompareFunc(rules) for _, update := range updates { if filterFunc(update, compareFunc) { valid = append(valid, update) } } return valid } func fixUpdate(update []int, compareFunc func(a, b int) bool) []int { fixUpdate := make([]int, len(update)) copy(fixUpdate, update) sorter := CustomSorter{ data: fixUpdate, compareFunc: compareFunc, } sort.Sort(sorter) return fixUpdate } func fixUpdates(updates [][]int, rules []Rule) [][]int { fixed := [][]int{} compareFunc := getCompareFunc(rules) for _, update := range updates { fixed = append(fixed, fixUpdate(update, compareFunc)) } return fixed } func contains(slice []int, element int) bool { for _, v := range slice { if v == element { return true } } return false } func getRulesMap(rules []Rule) map[int][]int { mustBeBefore := map[int][]int{} for _, rule := range rules { current := mustBeBefore[rule.After] if current == nil { current = []int{} } current = append(current, rule.Before) mustBeBefore[rule.After] = current } return mustBeBefore } func getMiddlePage(updates [][]int) []int { middlePages := []int{} for _, update := range updates { middle := (len(update) - 1) / 2 middlePages = append(middlePages, update[middle]) } return middlePages } func sum(values []int) int { sum := 0 for _, val := range values { sum += val } return sum } func main() { rules, err := readRulesFromFile("rules.txt") if err != nil { log.Fatalf("Error reading data: %s", err) } updates, err := readValuesFromFile("input.txt") if err != nil { log.Fatalf("Error reading data: %s", err) } validUpdates := filterUpdates(updates, rules, isValidUpdate) middlePages := getMiddlePage(validUpdates) total := sum(middlePages) fmt.Printf("Part 1: %d\n", total) invalidUpdates := filterUpdates(updates, rules, isInvalidUpdate) fixedUpdates := fixUpdates(invalidUpdates, rules) fixedMiddlePages := getMiddlePage(fixedUpdates) fixedTotal := sum(fixedMiddlePages) fmt.Printf("Part 2: %d\n", fixedTotal) }

Dold text

Hade definitivt behövt stÀdas upp, men fick iaf utforska en gnutta Go pÄ kuppen...

Visa signatur

Desktop spel m.m.: Ryzen 9800X3D || MSI X870 Tomahawk Wifi || MSI Ventus 3x 5080 || Gskill FlareX 6000 64GB || Kingston KC3000 2TB || Samsung 970 EVO Plus 2TB || Samsung 960 Pro 1TB || Fractal Torrent || Asus PG42UQ 4K OLED
Arbetsstation: Ryzen 7945HX || Minisforum BD790i || Asus Proart 4070 Ti Super || Kingston Fury Impact 5600 65 GB || WD SN850 2TB || Samsung 990 Pro 2TB || Fractal Ridge
Proxmox server: Ryzen 5900X || Asrock Rack X570D4I-2T || Kingston 64GB ECC || WD Red SN700 1TB || Blandning av WD Red / Seagate Ironwolf för lagring || Fractal Node 304

PermalÀnk
Medlem ★
●

Dag: 6
SprÄk: C#
Dag 6

Bruteforce for the win?

Rolig utmaning och förvÄnansvÀrt snabbt att lösa ÀndÄ.
Uppgift 2 kÀndes klurig men kunde nyttja resultatet frÄn Del 1 (vilka positioner som första omgÄngen gick över), sedan gÄ igenom varje position och byta ut mot en ny vÀgg. DÀrefter kördes "pathfinding"-delen pÄ nytt, dÀr den omskrivna delen handlade om att;

  1. Varje gÄng du nÄr en vÀgg sÄ lagrar du positionen, "vinkeln" (riktning) du hade in mot vÀggen och Àven vilken "vinkel" du skulle haft bort frÄn vÀggen.

  2. Kontrollera sedan om denna data redan finns sparad -i sÄ fall vet du att du har vart hÀr en gÄng tidigare och sÄledes Àr i en loop.

namespace Day_6; public class Solver { private enum Direction { Up, Right, Down, Left }; private record struct Point(int X, int Y) { } private record struct VisitedPointWithDirections(Point Point, Direction IncomingDirection, Direction OutgoingDirection); private static Point Move(Point p, Direction dir) => dir switch { Direction.Up => p with { Y = p.Y - 1 }, Direction.Down => p with { Y = p.Y + 1 }, Direction.Left => p with { X = p.X - 1 }, Direction.Right => p with { X = p.X + 1 }, _ => p }; private static bool IsPosInBounds(Point pos, int maxWidth, int maxHeight) => pos.X >= 0 && pos.Y >= 0 && pos.X < maxWidth && pos.Y < maxHeight; private static Direction RotateDirection(Direction dir) => dir switch { Direction.Up => Direction.Right, Direction.Down => Direction.Left, Direction.Left => Direction.Up, Direction.Right => Direction.Down, _ => Direction.Up }; private static Dictionary<Point, int> GetGuardVisitedPositions(string[,] grid, Point startPos) { var visitedPositions = new Dictionary<Point, int>(); var currentDir = Direction.Up; var currentPos = startPos; while (true) { var newPos = Move(currentPos, currentDir); if (!IsPosInBounds(newPos, grid.GetLength(0), grid.GetLength(1))) { visitedPositions[currentPos] = 1; break; } if (grid[newPos.Y, newPos.X] == "#") { currentDir = RotateDirection(currentDir); } else { visitedPositions[currentPos] = 1; currentPos = newPos; } } return visitedPositions; } private static int GetNumberOfLoopedRoutes(string[,] grid, Dictionary<Point, int> visitedPositions, Point startPos) { int numberOfLoopedRoutes = 0; for(int i = 0; i < visitedPositions.Count; i++) { var currentDir = Direction.Up; var currentPos = startPos; var currentIterPos = visitedPositions.ElementAt(i); if (currentIterPos.Key == startPos) continue; grid[currentIterPos.Key.Y, currentIterPos.Key.X] = "O"; var pointsWithDirections = new Dictionary<VisitedPointWithDirections, int>(); while (true) { var newPos = Move(currentPos, currentDir); if (!IsPosInBounds(newPos, grid.GetLength(0), grid.GetLength(1))) break; if (grid[newPos.Y, newPos.X] == "#" || grid[newPos.Y, newPos.X] == "O") { var newDir = RotateDirection(currentDir); var pointWithDirection = new VisitedPointWithDirections(currentPos, currentDir, newDir); if (!pointsWithDirections.TryAdd(pointWithDirection, 1)) { numberOfLoopedRoutes++; break; } currentDir = RotateDirection(currentDir); } else { currentPos = newPos; } } grid[currentIterPos.Key.Y, currentIterPos.Key.X] = "."; } return numberOfLoopedRoutes; } private static string[,] GetGrid(string input) { string[] lines = input.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); int height = lines.Length; int width = lines[0].Length; string[,] grid = new string[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { grid[y, x] = lines[y][x].ToString(); } } return grid; } public static int Run_PartOne(string input) { // Something in front; Turn 90 degrees to the right // Move forward // Add each position (including starting position) to a dictionary. var grid = GetGrid(input); var startPos = new Point(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { if (grid[y, x] == "^") startPos = new Point(x, y); } } var visitedPositions = GetGuardVisitedPositions(grid, startPos); var distinctPositions = visitedPositions.Keys.Count; return distinctPositions; } public static int Run_PartTwo(string input) { // Start by running PartOne to get all the positions visited. // Place a new obstacle in any of the visited positions (except the startPos) and run the loop detection: // // Record the direction, position and new direction every time you need to change direction. // If the data already exists, you know you've been here before and is thus in a loop. // Repeat until every visited position has been tried at least once (except startPos) var grid = GetGrid(input); var startPos = new Point(); for (int y = 0; y < grid.GetLength(0); y++) { for (int x = 0; x < grid.GetLength(1); x++) { if (grid[y, x] == "^") startPos = new Point(x, y); } } var visitedPositions = GetGuardVisitedPositions(grid, startPos); var loopedRoutes = GetNumberOfLoopedRoutes(grid, visitedPositions, startPos); return loopedRoutes; } }

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: 6
SprÄk: Java
GitHub

Koden ser fruktansvÀrd ut för tvÄan, men exekveringstiden Àr under sekunden pÄ min relativt slöa laptop sÄ fÄr vÀl vara nöjd med det.

Visa signatur

Starka Äsikter om onödiga saker.

PermalÀnk
Medlem ★
●

Dag 6 Endast Del 1 för psykets skull!
PHP via "Code Runner" i VSCode Terminal.

6.php - Endast Dag 6 Del 1 för psykets skull!

<?php include '_helpers.php'; $data = "https://adventofcode.com/2024/day/6/input"; $steps = 1; $distinctPositions = []; $map = generateXYCoordinates($data, "\n"); $startCoordinate = array_search("^", $map); $distinctPositions[] = $startCoordinate; $start = moveInOneDirectionInCoordinateUntilStopChar("TOP", $startCoordinate, $map, "#", $steps, $distinctPositions); while (is_array($start)) { $start = moveInOneDirectionInCoordinateUntilStopChar(turnRight90Degrees($start[1]), $start[0], $map, "#", $steps, $distinctPositions); if ($start == "FOUND") { break; } } echo "Steps: " . $steps;

_helpers.php

function generateXYCoordinates($data, $splitOn = "") { $coordinateSystem = []; if ($splitOn != "") { $data = explode("\n", $data); } if ($splitOn == "\n") { $data = array_map('rtrim', $data); } if (!is_array($data)) { throw new Exception("Data is not an array, please provide an array or a split on value."); } foreach ($data as $y => $row) { for ($x = 0; $x < strlen($row); $x++) { $coord = ($x + 1) . "," . ($y + 1); $coordinateSystem[$coord] = $row[$x]; } } return $coordinateSystem; } function getCharAt($coordinate, $coordinateSystem, $relativePosition = "", $relativeOffset = 0) { if ($relativePosition == "") { return isset($coordinateSystem[$coordinate]) ? $coordinateSystem[$coordinate] : "?"; } [$x, $y] = array_map('intval', explode(",", $coordinate)); switch ($relativePosition) { case "TOP": return isset($coordinateSystem[$x . "," . ($y - 1 - $relativeOffset)]) ? $coordinateSystem[$x . "," . ($y - 1 - $relativeOffset)] : "?"; case "BOTTOM": return isset($coordinateSystem[$x . "," . ($y + 1 + $relativeOffset)]) ? $coordinateSystem[$x . "," . ($y + 1 + $relativeOffset)] : "?"; case "LEFT": return isset($coordinateSystem[($x - 1 - $relativeOffset) . "," . $y]) ? $coordinateSystem[($x - 1 - $relativeOffset) . "," . $y] : "?"; case "RIGHT": return isset($coordinateSystem[($x + 1 + $relativeOffset) . "," . $y]) ? $coordinateSystem[($x + 1 + $relativeOffset) . "," . $y] : "?"; case "TOP LEFT": return isset($coordinateSystem[($x - 1 - $relativeOffset) . "," . ($y - 1 - $relativeOffset)]) ? $coordinateSystem[($x - 1 - $relativeOffset) . "," . ($y - 1 - $relativeOffset)] : "?"; case "TOP RIGHT": return isset($coordinateSystem[($x + 1 + $relativeOffset) . "," . ($y - 1 - $relativeOffset)]) ? $coordinateSystem[($x + 1 + $relativeOffset) . "," . ($y - 1 - $relativeOffset)] : "?"; case "BOTTOM LEFT": return isset($coordinateSystem[($x - 1 - $relativeOffset) . "," . ($y + 1 + $relativeOffset)]) ? $coordinateSystem[($x - 1 - $relativeOffset) . "," . ($y + 1 + $relativeOffset)] : "?"; case "BOTTOM RIGHT": return isset($coordinateSystem[($x + 1 + $relativeOffset) . "," . ($y + 1 + $relativeOffset)]) ? $coordinateSystem[($x + 1 + $relativeOffset) . "," . ($y + 1 + $relativeOffset)] : "?"; default: return "?"; } } function turnRight90Degrees($currentDirection) { switch ($currentDirection) { case "TOP": return "RIGHT"; case "RIGHT": return "BOTTOM"; case "BOTTOM": return "LEFT"; case "LEFT": return "TOP"; default: throw new Exception("Invalid direction provided, please provide a valid direction."); } } function moveInOneDirectionInCoordinateUntilStopChar($direction, $startCoordinate, $coordinateSystem, $stopAt = "", &$stepCounter = null, &$distinctPositions) { $currentCoordinate = $startCoordinate; $currentChar = getCharAt($currentCoordinate, $coordinateSystem); while ($currentChar != $stopAt) { [$x, $y] = extractCoordinates($currentCoordinate); $oldCoordinate = $currentCoordinate; switch ($direction) { case "TOP": $currentCoordinate = $x . "," . ($y - 1); break; case "BOTTOM": $currentCoordinate = $x . "," . ($y + 1); break; case "LEFT": $currentCoordinate = ($x - 1) . "," . $y; break; case "RIGHT": $currentCoordinate = ($x + 1) . "," . $y; break; case "TOP LEFT": $currentCoordinate = ($x - 1) . "," . ($y - 1); break; case "TOP RIGHT": $currentCoordinate = ($x + 1) . "," . ($y - 1); break; case "BOTTOM LEFT": $currentCoordinate = ($x - 1) . "," . ($y + 1); break; case "BOTTOM RIGHT": $currentCoordinate = ($x + 1) . "," . ($y + 1); break; default: throw new Exception("Invalid direction provided, please provide a valid direction."); } if (getCharAt($currentCoordinate, $coordinateSystem) == $stopAt) { echo "Obstacle found at: ($currentCoordinate) in direction $direction\n"; return [$oldCoordinate, $direction]; } elseif (getCharAt($currentCoordinate, $coordinateSystem) == "?") { echo "Found the exit at at: ($currentCoordinate) in direction $direction\n"; return "FOUND"; } . if (!isset($distinctPositions[$currentCoordinate])) { $distinctPositions[$currentCoordinate] = 1; $stepCounter++; } $currentChar = getCharAt($currentCoordinate, $coordinateSystem); echo "Moved ($direction|$currentCoordinate). Found: " . $currentChar . "| Steps: $stepCounter\n"; } return [$currentCoordinate, $direction]; }

Dold text

Jag kunde inte hÄlla mig att tjuvkika pÄ dagens och nÀr jag sÄg att det var koordinatbaserat sÄ kunde jag inte lÄta bli att prova att ÄteranvÀnda tidigare kod frÄn dag 3(?).

Det gÄr INTE att skriva "fulare" kod Àn jag dÄ jag Àr 100 % garanterat den minst erfarna aktiva deltagaren i denna trÄd. Statistiken talar emot att just jag skulle rÄka skriva elegantare kod Àn nÄgon annan hÀr!

Efter att jag först fick för högt svar sÄ misstÀnkte jag att med "distinct" sÄ innebar en sÀrskild sak att ocksÄ kontrollera. SÄ vid andra försöket blev Dag 6 Del 1 rÀtt och det fÄr vara bra sÄ.

Mvh,
WKF.

Visa signatur

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

PermalÀnk
Medlem ★
●
Skrivet av WebbkodsFrilansaren:

Jag kunde inte hÄlla mig att tjuvkika pÄ dagens och nÀr jag sÄg att det var koordinatbaserat sÄ kunde jag inte lÄta bli att prova att ÄteranvÀnda tidigare kod frÄn dag 3(?).

Det gÄr INTE att skriva "fulare" kod Àn jag dÄ jag Àr 100 % garanterat den minst erfarna aktiva deltagaren i denna trÄd. Statistiken talar emot att just jag skulle rÄka skriva elegantare kod Àn nÄgon annan hÀr!
Mvh,
WKF.

SlÀpp denna mentalitet om att "nÄgon annan alltid Àr bÀttre".

"If you compare yourself with others, you may become vain and bitter, for always there will be greater and lesser persons than yourself"

Du gör sÄ gott du kan och löser uppgifterna pÄ ditt sÀtt.
"Elegant"-kod Àr inte alltid nÄgot bra. Det kan vara rent av en mardröm att förvalta sÄdan kod, istÀllet för att den bara hade vart "lagom".

Se det som ett tillfÀlle att utvecklas och lÀra dig sjÀlv genom att kika pÄ andras lösningar. Kolla sedan tillbaka pÄ din kod och fundera kring hur du hade kunnat gjort just din kod bÀttre? Det gÄr Àven att inkludera ChatGPT/dylikt pÄ samma sÀtt. LÀs igenom svaret och fundera pÄ om detta hade kunnat anvÀndas i din lösning - istÀllet för att be om hela lösningen och bara klistra in.

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 WebbkodsFrilansaren:

AngÄende Dag 5 Del 2:

Ja, inte ens att skicka in bara ett array element i taget fungerade. ChunkSize 1 fungerar inte ens (en strÀng i taget) utan den kraschar pÄ kodraden: "$array[$i] = $array[$start_i];" i permutationsfunktionen.

// Function that generates permutations of an array to brute force all possible combinations function generatePermutations($array) { $result = []; $recurse = function ($array, $start_i = 0) use (&$result, &$recurse) { if ($start_i === count($array) - 1) { array_push($result, $array); } for ($i = $start_i; $i < count($array); $i++) { //Swap array value at $i and $start_i $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; //Recurse $recurse($array, $start_i + 1); //Restore old order $t = $array[$i]; $array[$i] = $array[$start_i]; $array[$start_i] = $t; } }; $recurse($array); return $result; } function solvePart2($invalidStrings, $testdata1) { $chunkSum = 0; foreach ($invalidStrings as $string) { $string = explode("|", $string); $permutations = generatePermutations($string); foreach ($permutations as $permutation) { $valid = true; foreach ($testdata1 as $rule) { $result = TwoArrayElementsAreInStringAndInValidOrder(implode("|", $permutation), $rule); if ($result === false) { $valid = false; break; } } if ($valid) { $chunkSum += ExtractMiddleArrayValue($permutation); $foundValid = true; break; } } } return $chunkSum; } $chunks = array_chunk($invalidStrings, 1); foreach ($chunks as $chunk) { $sumOfMiddleNumbers += solvePart2($chunk, $testdata1); unset($chunk); gc_collect_cycles(); }

Dold text

Jag undrar hur vanlig den sorteringsalgoritmen (verkar komma frÄn Grafteori) Àr i vardagslivet för utvecklare? Kanske databaser anvÀnder sig av sÄdana prestandaoptimerade sorteringsalgoritmer i vissa fall?

Och sĂ„ kĂ€nner jag mig smĂ„tt uppgiven hur din lösning Ă€r sĂ„ kort och Ă€ndĂ„ sĂ„ rĂ€tt medan min Ă€r sĂ„ lĂ„ng och samtidigt sĂ„ himla trasig eftersom den inte gĂ„r att köra?! đŸ„ČđŸ€Ł

Mvh,
WKF.

Skrivet av Ingetledigtnamn:

Det "topologiska" handlar om att A | B-reglerna beskriver en graf över hur vÀnsterleden beror pÄ högerleden, men vi kan strunta i det. TÀnk dig en sorteringsfunktion som, istÀllet för att jÀmföra de numeriska vÀrdena pÄ A och B, slÄr upp i reglerna om A Àr mindre Àn B (dvs A skall komma före B). Det hÀnder relativt ofta att man vill sortera pÄ andra saker Àn rena numeriska vÀrden eller strÀngar. Du kan rÀkna med att du behöver göra det under flera av de kommande uppgifterna i AoC

JÀttespoiler, lÀs inte denna om du inte redan har klarat uppgiften eller gett upp!

Tricket till min korta lösning Àr att sorteringsalgortimen och unika vÀrden i listorna gör att sorteringen Àr stabil, dvs att om vi sorterar en redan sorterad lista kommer resultatet bli exakt samma som input.

lists = alla listor
s1 = alla ordande listor i lists
s2 = alla oordnade listor i lists

I del 1 skall vi summera mittelementen i s1.
I del 2 skall vi summera mittelementen i sorted(s2).

Men nÀr en sorterad lista fortfarande Àr sorterad efter att vi sorterar den igen sÄ kan vi göra samma sak i bÄda fallen.

I del 1 skall vi summera mittelementen i sorted(s1).
I del 2 skall vi summera mittelementen i sorted(s2).

Nu kan vi iterera över lists (utan att dela upp i ordnade och oordnade), jÀmföra en lista (l1) med den den sorterade versionen av samma lista (l2=sorted(l1)). l1 == l2 addera till summan för del1, l1 !=l2 addera till summan för del 2.

Dold text

Ja, absolut...

LÀgger mitt tillÀgg i spoiler, vet inte om det Àr sÄ mycket spoiler men iaf...

Bara för att förtydliga en aspekt som kan vara hjÀlpsam ur ett vÀldigt praktiskt perspektiv:

De flesta sprÄk brukar ha nÄgon vettig sorteringsfunktion som duger i "standardfall" inbyggd (eller i standardbiblioteket t.ex.).

Och det fungerar dÄ typiskt genom att det du sjÀlv definierar Àr hur man ska jÀmföra tvÄ av listans element (dvs en funktion som svarar pÄ vilket av tvÄ element som ska hamna före det andra), vilket ju Àr det mest centrala i "beslutsprocessen" vid sortering. Och denna standardsortering ropar dÄ bara pÄ din funktion dÀr det behövs under sorteringsprocessen, sÄ kan du styra hur typ vad som helst ska sorteras.
Dvs, du behöver i praktiken oftast inte implementera sortering i sin helhet, bara definiera sorteringsordningen.

Och mer teoretiskt / om du vill implementera nÄgot sjÀlv: det finns ju massor av vÀlkÀnda och vÀldokumenterade sorteringsalgoritmer, sÄ om du ska göra nÄgot sjÀlv, titta pÄ hur dessa fungerar.
T.ex. https://en.wikipedia.org/wiki/Bubble_sort brukar vÀl kunna vara en bra introduktion till sortering eftersom den Àr enkel att visualisera, enkel att implementera, etc.

Dold text
Visa signatur

Desktop spel m.m.: Ryzen 9800X3D || MSI X870 Tomahawk Wifi || MSI Ventus 3x 5080 || Gskill FlareX 6000 64GB || Kingston KC3000 2TB || Samsung 970 EVO Plus 2TB || Samsung 960 Pro 1TB || Fractal Torrent || Asus PG42UQ 4K OLED
Arbetsstation: Ryzen 7945HX || Minisforum BD790i || Asus Proart 4070 Ti Super || Kingston Fury Impact 5600 65 GB || WD SN850 2TB || Samsung 990 Pro 2TB || Fractal Ridge
Proxmox server: Ryzen 5900X || Asrock Rack X570D4I-2T || Kingston 64GB ECC || WD Red SN700 1TB || Blandning av WD Red / Seagate Ironwolf för lagring || Fractal Node 304

PermalÀnk
Medlem ★
●
Skrivet av Pamudas:

Dag: 6
SprÄk: C#
Dag 6

Bruteforce for the win?

Rolig utmaning och förvÄnansvÀrt snabbt att lösa ÀndÄ.
Uppgift 2 kÀndes klurig men kunde nyttja resultatet frÄn Del 1 (vilka positioner som första omgÄngen gick över), sedan gÄ igenom varje position och byta ut mot en ny vÀgg. DÀrefter kördes "pathfinding"-delen pÄ nytt, dÀr den omskrivna delen handlade om att;

  1. Varje gÄng du nÄr en vÀgg sÄ lagrar du positionen, "vinkeln" (riktning) du hade in mot vÀggen och Àven vilken "vinkel" du skulle haft bort frÄn vÀggen.

  2. Kontrollera sedan om denna data redan finns sparad -i sÄ fall vet du att du har vart hÀr en gÄng tidigare och sÄledes Àr i en loop.

Dold text

Det rÀcker fullgott i det lÀget att lagra vilken riktning du har för en given position, kommer du in frÄn samma hÄll en gÄng till sÄ har du en loop.

Dold text

Dag 6, C#

using System.Diagnostics;
var lines = File.ReadAllLines("input.txt");
Dictionary<(int x, int y), char> grid = [];
for (int x = 0; x < lines.Length; x++)
for (int y = 0; y < lines[x].Length; y++)
grid[(x, y)] = lines[x][y];

char[] guard = ['^', '<', '>', 'v'];
var origin = grid.Where(x => guard.Contains(x.Value)).FirstOrDefault();
Stopwatch sw = Stopwatch.StartNew();
//part 1
(var cpos, var cval) = (origin.Key, origin.Value);
GuardLoops(grid, cpos, cval);
Console.WriteLine("Part 1: " + grid.Where(x => x.Value == 'X').Sum(x => 1));
Console.WriteLine("Part 1 time: " + sw.Elapsed);
sw.Restart();

// part 2
grid[origin.Key] = origin.Value;
var possibles = grid.Where(x => x.Value != '.');
int sumpart2 = 0;
Parallel.ForEach(possibles, pos =>
{
var gridcopy = grid.ToDictionary(entry => entry.Key, entry => entry.Value);
gridcopy[pos.Key] = '#';
(var cpos, var cval) = (origin.Key, origin.Value);
if (GuardLoops(gridcopy, cpos, cval))
Interlocked.Increment(ref sumpart2);
});
sw.Stop();
Console.WriteLine("Part 2: " + sumpart2);
Console.WriteLine("Part 2 time: " + sw.Elapsed);

static bool GuardLoops(Dictionary<(int x, int y), char> grid, (int x, int y) cpos, char cval)
{
HashSet<((int x, int y), char)> steps = [];
while (true)
{
(int x, int y) npos = cval switch
{
'^' => ((cpos.x - 1), (cpos.y)),
'>' => ((cpos.x), (cpos.y + 1)),
'v' => ((cpos.x + 1), (cpos.y)),
_ => ((cpos.x), (cpos.y - 1)), // '<'
};
if (grid.TryGetValue(npos, out char nval))
{
if (nval == '#')
{
cval = cval switch
{
'^' => '>',
'>' => 'v',
'v' => '<',
_ => '^' // '<'
};
}
else
{
if (steps.Contains((cpos, cval)))
return true;
else
steps.Add((cpos, cval));
grid[cpos] = 'X';
cpos = npos;
}
}
else
{
grid[cpos] = 'X';
return false;
}
}
}

Dold text
mer optimerad kod
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 ★
●
Skrivet av kode:

Det rÀcker fullgott i det lÀget att lagra vilken riktning du har för en given position, kommer du in frÄn samma hÄll en gÄng till sÄ har du en loop.

Dold text

Jag insÄg faktiskt det efter jag löst uppgiften

Optimering lÀmnar jag till en annan gÄng, t.ex. köra parallellt passar utmÀrkt (likt din lösning).

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