ค้นหาปีด้วยจำนวนประชากรสูงสุด (วิธีที่มีประสิทธิภาพที่สุด)


9

รับสองอาร์เรย์ $birthsมีรายการปีเกิดแสดงเมื่อมีคนเกิดและ$deathsมีรายการปีแห่งความตายที่ระบุเมื่อมีคนตายเราจะหาปีที่ประชากรสูงสุดได้อย่างไร

ตัวอย่างเช่นกำหนดอาร์เรย์ต่อไปนี้:

$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];

ปีที่ประชากรสูงสุดควรเป็น1996เพราะ3ประชาชนยังมีชีวิตอยู่ในช่วงปีนั้นซึ่งเป็นประชากรที่สูงที่สุดนับจากทุกปี

นี่คือคณิตศาสตร์ที่ใช้อยู่ในนั้น:

| เกิด | ความตาย | ประชากร
| ------- | ------- | ------------ |
| 1981 | | 1 |
| 2527 | | 2 |
| 2527 | 2527 | 2 |
| 2534 | 2534 | 2 |
| 2539 | | 3 |

สมมติฐาน

เราสามารถสรุปได้อย่างปลอดภัยว่าปีที่ใครบางคนเกิดมาประชากรสามารถเพิ่มขึ้นได้หนึ่งปีและปีที่บางคนเสียชีวิตประชากรสามารถลดลงได้หนึ่งคน ดังนั้นในตัวอย่างนี้ 2 คนเกิดในปี 1984 และอีก 1 คนเสียชีวิตในปี 1984 ซึ่งหมายความว่าจำนวนประชากรที่เพิ่มขึ้น 1 ในปีนั้น

เราสามารถสรุปได้อย่างปลอดภัยว่าจำนวนผู้เสียชีวิตจะไม่เกินจำนวนการเกิดและจะไม่มีการเสียชีวิตเกิดขึ้นเมื่อประชากรอยู่ที่ 0

เราสามารถสันนิษฐานได้อย่างปลอดภัยว่าหลายปีในทั้งสองนี้$deathsและ$birthsจะไม่เป็นค่าลบหรือทศนิยม ( พวกมันมักเป็นจำนวนเต็มบวกมากกว่า 0 )

เราไม่สามารถสรุปได้ว่าอาร์เรย์จะถูกเรียงลำดับหรือจะไม่มีค่าซ้ำกัน

ความต้องการ

เราจะต้องเขียนฟังก์ชั่นเพื่อคืนปีที่มีประชากรมากที่สุดเกิดขึ้นเนื่องจากทั้งสองอาร์เรย์เป็นอินพุต ฟังก์ชั่นอาจจะกลับมา0, false, ""หรือNULL ( ค่า falsey ใด ๆ ที่เป็นที่ยอมรับได้ ) ถ้าอาร์เรย์การป้อนข้อมูลที่ว่างเปล่าหรือถ้าประชากรที่เป็นเสมอที่ 0 ตลอด หากจำนวนประชากรสูงสุดเกิดขึ้นในหลายปีฟังก์ชันอาจส่งคืนปีแรกที่มีจำนวนประชากรสูงถึงหรือในปีต่อ ๆ มา

ตัวอย่างเช่น:

$births = [1997, 1997, 1997, 1998, 1999];
$deaths = [1998, 1999];

/* The highest population was 3 on 1997, 1998 and 1999, either answer is correct */

นอกจากนี้รวมถึงBig Oของโซลูชันจะเป็นประโยชน์


ความพยายามที่ดีที่สุดของฉันในการทำเช่นนี้จะเป็นดังนี้:

function highestPopulationYear(Array $births, Array $deaths): Int {

    sort($births);
    sort($deaths);

    $nextBirthYear = reset($births);
    $nextDeathYear = reset($deaths);

    $years = [];
    if ($nextBirthYear) {
        $years[] = $nextBirthYear;
    }
    if ($nextDeathYear) {
        $years[] = $nextDeathYear;
    }

    if ($years) {
        $currentYear = max(0, ...$years);
    } else {
        $currentYear = 0;
    }

    $maxYear = $maxPopulation = $currentPopulation = 0;

    while(current($births) !== false || current($deaths) !== false || $years) {

        while($currentYear === $nextBirthYear) {
            $currentPopulation++;
            $nextBirthYear = next($births);
        }

        while($currentYear === $nextDeathYear) {
            $currentPopulation--;
            $nextDeathYear = next($deaths);
        }

        if ($currentPopulation >= $maxPopulation) {
            $maxPopulation = $currentPopulation;
            $maxYear = $currentYear;
        }

        $years = [];

        if ($nextBirthYear) {
            $years[] = $nextBirthYear;
        }
        if ($nextDeathYear) {
            $years[] = $nextDeathYear;
        }
        if ($years) {
            $currentYear = min($years);
        } else {
            $currentYear = 0;
        }
    }

    return $maxYear;
}

อัลกอริทึมด้านบนควรทำงานในเวลาพหุนามเนื่องจากมันแย่ที่สุดO(((n log n) * 2) + k)โดยที่nจำนวนขององค์ประกอบที่จะเรียงลำดับจากแต่ละอาร์เรย์และkเป็นจำนวนปีเกิด ( เนื่องจากเรารู้ว่าkเป็นเสมอk >= y ) โดยที่yเป็นจำนวนปีการเสียชีวิต อย่างไรก็ตามฉันไม่แน่ใจว่ามีวิธีแก้ปัญหาที่มีประสิทธิภาพมากกว่านี้หรือไม่

ความสนใจของฉันล้วนๆในBig O ที่ปรับปรุงใหม่ของความซับซ้อนในการคำนวณตามอัลกอริทึมที่มีอยู่ ความซับซ้อนของหน่วยความจำไม่เกี่ยวข้อง ไม่เป็นการเพิ่มประสิทธิภาพรันไทม์ อย่างน้อยก็ไม่ได้เป็นความกังวลหลัก การปรับแต่งรันไทม์เล็กน้อย / หลักใด ๆ ยินดีต้อนรับ แต่ไม่ใช่ปัจจัยสำคัญที่นี่


2
เมื่อคุณมีวิธีแก้ปัญหาการทำงานสิ่งนี้จะเหมาะกับcodereview.stackexchange.comหรือไม่
Nigel Ren

1
คำถามคือการหาทางออกที่มีประสิทธิภาพที่สุดไม่จำเป็นต้องแก้ปัญหาการทำงานใด ๆ ฉันคิดว่ามันใช้ได้อย่างสมบูรณ์ใน SO
Sherif

1
ฉันไม่ได้บอกว่ามันไม่ถูกต้องใน SO (ฉันจะลงคะแนนให้ปิดในกรณีนั้น) ฉันแค่สงสัยว่าคุณอาจได้รับการตอบสนองมากขึ้นเกี่ยวกับ CR
Nigel Ren

@NigelRen ฉันไม่เห็นอันตรายในการลอง แม้ว่าฉันจะอยากจะเปิดทิ้งไว้สักสองสามวัน หากไม่ได้รับคำตอบฉันจะเอาเงินรางวัลลงไป
Sherif

1
ดังนั้นตัวเองจึงมีคำถามปัญหามากมายหากคุณค้นหาคำหลักที่ตายเกิด การปรับปรุงราคาถูกจะเป็นการปรับปรุงการเรียงลำดับ: สร้างอาร์เรย์ของความยาวช่วงเวลาของการเกิด / ความตาย (แต่ละเซลล์คือวันที่ที่เก็บค่า 0 ตามค่าเริ่มต้น) เพิ่ม 1 หรือกวางตัวที่ 1 ลงในเซลล์ที่เกี่ยวกับการเกิดและความตายจากนั้นจึงรวบรวมผลรวมและรักษาผลรวมสูงสุดที่พบ
grodzi

คำตอบ:


4

ฉันคิดว่าเราสามารถมีO(n log n)เวลากับO(1)พื้นที่เพิ่มเติมโดยการเรียงลำดับครั้งแรกจากนั้นรักษาประชากรปัจจุบันและระดับโลกสูงสุดเมื่อเราย้ำ ฉันพยายามที่จะใช้ปีปัจจุบันเป็นจุดอ้างอิง แต่ตรรกะยังดูยุ่งยากเล็กน้อยดังนั้นฉันไม่แน่ใจว่ามันได้ผลจริง หวังว่าจะสามารถให้แนวคิดเกี่ยวกับวิธีการ

รหัส JavaScript (ยินดีต้อนรับตัวนับตัวอย่าง / ข้อบกพร่อง)

function f(births, deaths){
  births.sort((a, b) => a - b);
  deaths.sort((a, b) => a - b);

  console.log(JSON.stringify(births));
  console.log(JSON.stringify(deaths));
  
  let i = 0;
  let j = 0;
  let year = births[i];
  let curr = 0;
  let max = curr;

  while (deaths[j] < births[0])
    j++;

  while (i < births.length || j < deaths.length){
    while (year == births[i]){
      curr = curr + 1;
      i = i + 1;
    }
    
    if (j == deaths.length || year < deaths[j]){
      max = Math.max(max, curr);
      console.log(`year: ${ year }, max: ${ max }, curr: ${ curr }`);
    
    } else if (j < deaths.length && deaths[j] == year){
      while (deaths[j] == year){
        curr = curr - 1;
        j = j + 1;
      }
      max = Math.max(max, curr);
      console.log(`year: ${ year }, max: ${ max }, curr: ${ curr }`);
    }

    if (j < deaths.length && deaths[j] > year && (i == births.length || deaths[j] < births[i])){
      year = deaths[j];
      while (deaths[j] == year){
        curr = curr - 1;
        j = j + 1;
      }
      console.log(`year: ${ year }, max: ${ max }, curr: ${ curr }`);
    }

    year = births[i];
  }
  
  return max;
}

var input = [
  [[1997, 1997, 1997, 1998, 1999],
  [1998, 1999]],
  [[1, 2, 2, 3, 4],
  [1, 2, 2, 5]],
  [[1984, 1981, 1984, 1991, 1996],
  [1991, 1984, 1997]],
  [[1984, 1981, 1984, 1991, 1996],
  [1991, 1982, 1984, 1997]]
]

for (let [births, deaths] of input)
  console.log(f(births, deaths));

หากช่วงปีmนั้นตามลำดับnเราสามารถจัดเก็บจำนวนสำหรับแต่ละปีในช่วงและมีO(n)ความซับซ้อนของเวลา หากเราต้องการจินตนาการเราก็อาจมีO(n * log log m)ความซับซ้อนของเวลาด้วยการใช้Y-fast trieที่ช่วยให้ค้นหาต่อไปได้O(log log m)ทันเวลา


1. ขอบคุณสำหรับการสอนฉันให้มีชีวิตอยู่ใน Y-fast trie เกี่ยวกับ algo: ไม่จำเป็นต้องตรวจสอบค่าสูงสุดหลังจากลดลง หลังจากเพิ่มขึ้นเท่านั้น สุดท้ายในขณะที่บล็อกไม่จำเป็น: พิจารณาการเรียงลำดับสองรายการที่เรียงลำดับ: คุณเพียงแค่ต้องมีหัวของทั้งสอง (i, j), เลือกหัวของแต่ละคนและเลื่อนไปหนึ่งอันที่เล็กกว่า if(birth_i < death_j){//increment stuff + check max} else{//decrement}; birth_i||=infty; death_j||=infty. min(birthSize, deathSize)นอกจากนี้คุณสามารถย้ำถึง ถ้านาทีเกิดให้หยุด ถ้านาทีคือความตาย (น่าสงสัย .. ) ให้หยุดและตรวจสอบ(max + birth.length-i)
grodzi

@grodzi ฉันเริ่มพิจารณาการเรียงแบบผสาน แต่สรุปว่าสิ่งนี้ต้องการการจัดการเป็นพิเศษเพราะการทำซ้ำรวมถึงลำดับของการเกิดกับความตายมีผลต่อการนับอย่างไร ขณะที่ลูปสุดท้ายดูเหมือนว่าจำเป็นสำหรับฉันเมื่อมีปีแห่งความตายที่ไม่มีใครเทียบได้กับปีเกิด คุณถูกต้องว่าไม่จำเป็นสูงสุดในลูปนั้น
גלעדברקן

@ גלעדברקןใช้ bucket sort สำหรับเวลาเชิงเส้น
เดฟ

ฉันได้ระบุแนวคิดนี้ไว้ในคำตอบของฉันแล้ว "ถ้าช่วงปี m อยู่ในคำสั่งของ n เราสามารถจัดเก็บค่านับสำหรับแต่ละปีในช่วงและมีความซับซ้อนของเวลา O (n)"
גלעדברקן

นี่ไม่ใช่ประสิทธิภาพฉันไม่รู้ว่าทำไมให้รางวัลกับคุณฮ่าฮ่าฮ่า
Emiliano

4

เราสามารถแก้ปัญหานี้ในเวลาเชิงเส้นด้วยการจัดเรียงถัง สมมุติว่าขนาดของอินพุตคือ n และช่วงของปีคือ m

O(n): Find the min and max year across births and deaths.
O(m): Create an array of size max_yr - min_yr + 1, ints initialized to zero. 
      Treat the first cell of the array as min_yr, the next as min_yr+1, etc...
O(n): Parse the births array, incrementing the appropriate index of the array. 
      arr[birth_yr - min_yr] += 1
O(n): Ditto for deaths, decrementing the appropriate index of the array.
      arr[death_yr - min_yr] -= 1
O(m): Parse your array, keeping track of the cumulative sum and its max value.

จำนวนสะสมมากที่สุดคือคำตอบของคุณ

เวลาทำงานคือ O (n + m) และพื้นที่เพิ่มเติมที่จำเป็นคือ O (m)

นี่คือวิธีแก้ปัญหาเชิงเส้นใน n ถ้า m เป็น O (n); กล่าวคือหากช่วงปีไม่เติบโตเร็วกว่าจำนวนการเกิดและการเสียชีวิต เกือบจะเป็นจริงสำหรับข้อมูลโลกแห่งความจริง


1
คุณช่วยเพิ่มการใช้งานได้ไหม
Sherif

1
@Sherif Implementation ถูกทิ้งไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน ... มันไม่สำคัญอยู่ดี มีอะไรไม่ชัดเจน?
เดฟ

ฉันจะทราบว่าเนื่องจากความละเอียดของคุณคือปีมีความกำกวมอยู่บ้าง ในการที่เราวัดประชากรอย่างมีประสิทธิภาพ ณ สิ้นปีและอาจมีบางจุดอื่น ๆ ในช่วงกลางปีที่ประชากรสูงขึ้นเนื่องจากช่วงเวลาของการเกิดและการเสียชีวิต
เดฟ

1
เวลาเชิงเส้นเป็นอย่างไรถ้าเราต้องแยก "อาร์เรย์ขนาด max_yr - min_yr + 1" (cc @Sherif)
גלעדברקן

1
@Dave: ความซับซ้อนไม่ใช่ O (2n) สำหรับแต้ม 1 และ 2 หรือไม่? 1. วนซ้ำทุกครั้งที่เกิด + ความตาย: O(n): Find the min and max year across births and deaths 2. วนซ้ำอีกครั้งตลอดการเกิดทั้งหมด + ความตาย: O(n): Parse the births+death array, incrementing the appropriate index of the array จากนั้นทำ: O (m): แยกอาร์เรย์ของคุณติดตามผลรวมสะสมและค่าสูงสุด (คุณไม่จำเป็นต้องแยกวิเคราะห์อาร์เรย์นี้ - คุณสามารถติดตาม MAX ได้ในขณะที่เพิ่มดัชนีใน 2)
Antony

3

ก่อนอื่นให้รวมการเกิดและความตายลงในแผนที่ ( year => population change) เรียงลำดับตามคีย์และคำนวณจำนวนประชากรที่วิ่งอยู่เหนือนั้น

นี่ควรประมาณโดยประมาณO(2n + n log n)ซึ่งnเป็นจำนวนการเกิด

$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];

function highestPopulationYear(array $births, array $deaths): ?int
{
    $indexed = [];

    foreach ($births as $birth) {
        $indexed[$birth] = ($indexed[$birth] ?? 0) + 1;
    }

    foreach ($deaths as $death) {
        $indexed[$death] = ($indexed[$death] ?? 0) - 1;
    }

    ksort($indexed);

    $maxYear = null;
    $max = $current = 0;

    foreach ($indexed as $year => $change) {
        $current += $change;
        if ($current >= $max) {
            $max = $current;
            $maxYear = $year;
        }
    }

    return $maxYear;
}

var_dump(highestPopulationYear($births, $deaths));

อย่างที่ฉันเห็น: ด้วยn = จำนวนเหตุการณ์ (เกิด + ตาย) และm = จำนวนเหตุการณ์ปี (ปีที่มีเกิดหรือตาย) นี่จะเป็นO (n + m log m)จริง ๆ ถ้าn >> ม. - นี้ถือได้ว่าเป็นO (n) หากคุณมีการเกิดและการเสียชีวิตนับพันล้านครั้งในช่วงระยะเวลา 100 ปีการเรียงลำดับอาเรย์ที่มีองค์ประกอบ 100 รายการksort($indexed)จะกลายเป็นสิ่งที่ไม่เกี่ยวข้อง
Paul Spiegel

$indexed = array_count_values($births);คุณสามารถประมวลผลที่เกิดกับ
ไนเจล Ren

3

ฉันแก้ไขปัญหานี้ด้วยความต้องการหน่วยความจำของO(n+m)[ในกรณีที่เลวร้ายที่สุดกรณีที่ดีที่สุดO(n)]

O(n logn)และความซับซ้อนของเวลา

นี่n & mคือความยาวของbirthsและdeathsอาร์เรย์

ฉันไม่รู้ PHP หรือจาวาสคริปต์ ฉันใช้มันกับJavaและตรรกะนั้นง่ายมาก แต่ฉันเชื่อว่าความคิดของฉันสามารถนำไปใช้ในภาษาเหล่านั้นได้เช่นกัน

รายละเอียดเทคนิค:

ฉันใช้TreeMapโครงสร้างJava เพื่อเก็บบันทึกการเกิดและการเสียชีวิต

TreeMapแทรกข้อมูลที่เรียงลำดับ ( ตามคีย์ ) เป็นคู่ (คีย์, ค่า) คีย์ที่นี่คือปีและค่าคือผลรวมสะสมของการเกิดและการตาย (ลบสำหรับการเสียชีวิต)

เราไม่จำเป็นต้องใส่ค่าความตายที่เกิดขึ้นหลังจากปีเกิดสูงสุด

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

ตัวอย่างอินพุตและเอาต์พุต: 1

Births: [1909, 1919, 1904, 1911, 1908, 1908, 1903, 1901, 1914, 1911, 1900, 1919, 1900, 1908, 1906]

Deaths: [1910, 1911, 1912, 1911, 1914, 1914, 1913, 1915, 1914, 1915]

Year counts Births: {1900=2, 1901=1, 1903=1, 1904=1, 1906=1, 1908=3, 1909=1, 1911=2, 1914=1, 1919=2}

Year counts Birth-Deaths combined: {1900=2, 1901=1, 1903=1, 1904=1, 1906=1, 1908=3, 1909=1, 1910=-1, 1911=0, 1912=-1, 1913=-1, 1914=-2, 1915=-2, 1919=2}

Yearwise population: {1900=2, 1901=3, 1903=4, 1904=5, 1906=6, 1908=9, 1909=10, 1910=9, 1911=9, 1912=8, 1913=7, 1914=5, 1915=3, 1919=5}

maxPopulation: 10
yearOfMaxPopulation: 1909

ตัวอย่างอินพุตและเอาต์พุต: 2

Births: [1906, 1901, 1911, 1902, 1905, 1911, 1902, 1905, 1910, 1912, 1900, 1900, 1904, 1913, 1904]

Deaths: [1917, 1908, 1918, 1915, 1907, 1907, 1917, 1917, 1912, 1913, 1905, 1914]

Year counts Births: {1900=2, 1901=1, 1902=2, 1904=2, 1905=2, 1906=1, 1910=1, 1911=2, 1912=1, 1913=1}

Year counts Birth-Deaths combined: {1900=2, 1901=1, 1902=2, 1904=2, 1905=1, 1906=1, 1907=-2, 1908=-1, 1910=1, 1911=2, 1912=0, 1913=0}

Yearwise population: {1900=2, 1901=3, 1902=5, 1904=7, 1905=8, 1906=9, 1907=7, 1908=6, 1910=7, 1911=9, 1912=9, 1913=9}

maxPopulation: 9
yearOfMaxPopulation: 1906

ที่นี่ความตายเกิดขึ้น ( 1914 & later) หลังจากปีเกิดที่ผ่านมา1913ไม่นับรวมเลยเพื่อหลีกเลี่ยงการคำนวณที่ไม่จำเป็น

สำหรับข้อมูลทั้งหมด10 million(การเกิดและการเสียชีวิตรวมกัน) ขึ้นไป1000 years rangeโปรแกรมก็ใช้เวลา3 sec.จนจบ

หากข้อมูลที่มีขนาดเดียวกันกับมันต้องใช้เวลา100 years range1.3 sec

อินพุตทั้งหมดจะถูกนำแบบสุ่ม


1
$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];
$years = array_unique(array_merge($births, $deaths));
sort($years);

$increaseByYear = array_count_values($births);
$decreaseByYear = array_count_values($deaths);
$populationByYear = array();

foreach ($years as $year) {
    $increase = $increaseByYear[$year] ?? 0;
    $decrease = $decreaseByYear[$year] ?? 0;
    $previousPopulationTally = end($populationByYear);
    $populationByYear[$year] = $previousPopulationTally + $increase - $decrease;
}

$maxPopulation = max($populationByYear);
$maxPopulationYears = array_keys($populationByYear, $maxPopulation);

$maxPopulationByYear = array_fill_keys($maxPopulationYears, $maxPopulation);
print_r($maxPopulationByYear);

นี่จะอธิบายถึงความเป็นไปได้ของปีที่ถูกผูกเช่นเดียวกับปีที่เสียชีวิตของใครบางคนไม่ตรงกับการเกิดของใครบางคน


คำตอบนี้ไม่ได้พยายามให้คำอธิบาย Big O เชิงวิชาการที่ OP ร้องขอ
mickmackusa

0

หน่วยความจำที่ชาญฉลาดมันคือการเก็บcurrentPopulationและcurrentYearคำนวณ การเริ่มต้นโดยการเรียงลำดับทั้งสอง$birthsและ$deathsอาร์เรย์เป็นจุดที่ดีมากเนื่องจากการเรียงลำดับฟองไม่ใช่งานที่หนัก แต่ยังอนุญาตให้ตัดมุมได้:

<?php

$births = [1997, 1999, 2000];
$deaths = [2000, 2001, 2001];

function highestPopulationYear(array $births, array $deaths): Int {

    // sort takes time, but is neccesary for futher optimizations
    sort($births);
    sort($deaths);

    // first death year is a first year where population might decrase 
    // sorfar max population
    $currentYearComputing = $deaths[0];

    // year before first death has potential of having the biggest population
    $maxY = $currentYearComputing-1;

    // calculating population at the begining of the year of first death, start maxPopulation
    $population = $maxPop = count(array_splice($births, 0, array_search($deaths[0], $births)));

    // instead of every time empty checks: `while(!empty($deaths) || !empty($births))`
    // we can control a target time. It reserves a memory, but this slot is decreased
    // every iteration.
    $iterations = count($deaths) + count($births);

    while($iterations > 0) {
        while(current($births) === $currentYearComputing) {
            $population++;
            $iterations--;
            array_shift($births); // decreasing memory usage
        }

        while(current($deaths) === $currentYearComputing) {
            $population--;
            $iterations--;
            array_shift($deaths); // decreasing memory usage
        }

        if ($population > $maxPop) {
            $maxPop = $population;
            $maxY = $currentYearComputing;
        }

        // In $iterations we have a sum of birth/death events left. Assuming all 
        // are births, if this number added to currentPopulation will never exceed
        // current maxPoint, we can break the loop and save some time at cost of
        // some memory.
        if ($maxPop >= ($population+$iterations)) {
            break;
        }

        $currentYearComputing++;
    }

    return $maxY;
}

echo highestPopulationYear($births, $deaths);

ไม่กระตือรือร้นในการดำน้ำในBig Oสิ่งที่ทิ้งไว้ให้คุณ

นอกจากนี้หากคุณค้นพบcurrentYearComputingวนซ้ำทุกครั้งคุณสามารถเปลี่ยนลูปเป็นifข้อความสั่งและปล่อยด้วยการวนซ้ำเพียงครั้งเดียว

    while($iterations > 0) {

        $changed = false;

        if(current($births) === $currentYearComputing) {
            // ...
            $changed = array_shift($births); // decreasing memory usage
        }

        if(current($deaths) === $currentYearComputing) {
            // ...
            $changed = array_shift($deaths); // decreasing memory usage
        }

        if ($changed === false) {
            $currentYearComputing++;
            continue;
        }

การเปลี่ยนอาร์เรย์เป็นตัวเลือกที่ดีสำหรับหน่วยความจำ แต่ไม่ใช่สำหรับประสิทธิภาพโปรดตรวจสอบcmljnelson.blog/2018/10/16/phps-array_shift-performance
Emiliano

คุณสามารถเรียงลำดับจากมากไปน้อยไปพร้อมกับลดลงแทนที่จะเพิ่มขึ้นและกับป๊อปแทนการเปลี่ยน
yergo

0

ฉันเติมความสะดวกสบายของโซลูชันนี้ความซับซ้อน Big O คือ n + m

<?php
function getHighestPopulation($births, $deaths){
    $max = [];
    $currentMax = 0;
    $tmpArray = [];

    foreach($deaths as $key => $death){
        if(!isset($tmpArray[$death])){
            $tmpArray[$death] = 0;    
        }
        $tmpArray[$death]--;
    }
    foreach($births as $k => $birth){
        if(!isset($tmpArray[$birth])){
            $tmpArray[$birth] = 0;
        }
        $tmpArray[$birth]++;
        if($tmpArray[$birth] > $currentMax){
            $max = [$birth];
            $currentMax = $tmpArray[$birth];
        } else if ($tmpArray[$birth] == $currentMax) {
            $max[] = $birth;
        }
    }

    return [$currentMax, $max];
}

$births = [1997, 1997, 1997, 1998, 1999];
$deaths = [1998, 1999];

print_r (getHighestPopulation($births, $deaths));
?>

ไม่ควร$tmpArray--จะเป็น$tmpArray[$death]--? โปรดทดสอบด้วย$births=[1997,1997,1998]; $deaths=[];- มันกลับมา1998ตามที่ควรหรือไม่
Paul Spiegel

ใช่คุณพูดถูก
Emiliano

รหัสนี้ไม่เพียง แต่ล้มเหลวในกรณีขอบซับซ้อน แต่ก็ล้มเหลวในการที่ง่ายที่สุดของกรณีเช่นได้รับการป้อนข้อมูลอาร์เรย์$births = [3,1,2,1,3,3,2]และ$deaths = [2,3,2,3,3,3]ฉันจะคาดหวังที่จะได้รับกลับมา2เป็นปีของประชากรสูงที่สุด 1แต่ผลตอบแทนที่รหัสของคุณ ในความเป็นจริงรหัสของคุณล้มเหลว 9 ใน 15 ของการทดสอบหน่วยของฉัน ฉันไม่เพียง แต่ยอมรับไม่ได้ว่านี่เป็นคำตอบที่มีประสิทธิภาพที่สุดแต่ฉันก็ไม่สามารถยอมรับได้ว่าเป็นคำตอบที่มีประสิทธิภาพเนื่องจากไม่ได้ผลเลย
Sherif

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

0

หนึ่งในวิธีที่ง่ายและชัดเจนที่สุดสำหรับปัญหาของคุณ

$births = [1909, 1919, 1904, 1911, 1908, 1908, 1903, 1901, 1914, 1911, 1900, 1919, 1900, 1908, 1906];
$deaths = [1910, 1911, 1912, 1911, 1914, 1914, 1913, 1915, 1914, 1915];

/* for generating 1 million records

for($i=1;$i<=1000000;$i++) {
    $births[] = rand(1900, 2020);
    $deaths[] = rand(1900, 2020);
}
*/

function highestPopulationYear(Array $births, Array $deaths): Int {
    $start_time = microtime(true); 
    $population = array_count_values($births);
    $deaths = array_count_values($deaths);

    foreach ($deaths as $year => $death) {
        $population[$year] = ($population[$year] ?? 0) - $death;
    }
    ksort($population, SORT_NUMERIC);
    $cumulativeSum = $maxPopulation = $maxYear = 0;
    foreach ($population as $year => &$number) {
        $cumulativeSum += $number;
        if($maxPopulation < $cumulativeSum) {
            $maxPopulation = $cumulativeSum;
            $maxYear = $year;
        }
    }
    print " Execution time of function = ".((microtime(true) - $start_time)*1000)." milliseconds"; 
    return $maxYear;
}

print highestPopulationYear($births, $deaths);

ผลลัพธ์ :

1909

ความซับซ้อน :

O(m + log(n))

เวลาในการดำเนินการ 1 ล้านระเบียนเป็นเพียง29.64 milliseconds
Ronak Dhoot

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