JavaRush /Курсы /Harvard CS50 /Задание 1: Search

Задание 1: Search

Harvard CS50
3 уровень , 13 лекция
Открыта

Пришло время для кое-чего интересного! Обратите внимание, find.c вызывает search, функцию, объявленную в helpers.h. К сожалению, мы забыли реализовать эту функцию полностью в helpers.c! Надо отметить, что мы могли бы поместить содержимое helpers.h и helpers.c в один find.c. Но иногда лучше разделять программы на несколько файлов. Особенно если некоторые функции, точнее функции-утилиты, могут оказаться полезными и для других программ. Как, например, функции из библиотеки CS50. 

Взгляните на helpers.c и вы увидите, что search всегда возвращает значение false, независимо от того, занесено ли данное значение value в values. Перепишите search таким образом, чтобы она использовала линейный поиск, возвращая true, если value входит в values и false, если value не входит в values. Позаботьтесь о том, чтобы вернуть false сразу, если n не является положительным.

Когда вы будете готовы к проверке правильности, попробуйте вызвать следующую команду: 

./generate 1000 50 | ./find 127

Поскольку одно из чисел, выведенных с помощью generate, когда «засеивали» 50, равно 127, ваш код должен найти „иголку“! Для контраста попробуйте выполнить и такую команду: 

./generate 1000 50 | ./find 128

Поскольку 128 не входит во множество чисел, выведенных функцией generate, когда «засеивали» 50, ваш код не должен найти «иглу». Проведите и другие тесты, запуская generate с каким-то семенем, анализируя выходные данные, и ища «иголку», которая должна быть в «стогу». 

Обратите внимание, что main в find.c написана таким образом, что find возвращает 0, если «игла» найдена, иначе — возвращает 1. Вы можете проверить так называемый „выходной код“, который main возвращает при выполнении

echo $?

После запуска некоторых других команд. Например, если ваша реализация search — правильна, если вы запустите 

./generate 1000 50 | ./find 127
echo $?

Вы увидите 0, пока 127 есть среди тех 1000 чисел, выведенных с помощью generate с семенем 50, поэтому написанный вами поиск search должен возвратить true. В этом случае main (написанный нами) должен вернуть 0 (то есть завершить работу). Если же вы зададите следующие входные данные

./generate 1000 50 | ./find 128
echo $?

Вы увидите 1, поскольку 128 не находится среди тех 1000 чисел, которые были выведены с помощью generate с семенем 50. В этом случае search (написанный вами) вернет false, и main (написанный нами) вернет 1 (и с тем закончит работу). Сечёте логику? 

Когда всё будет готово для проверки с помощью check50, выполните такую команду:

check50 2015.fall.pset3.find helpers.c

Кстати, не стоит привыкать тестировать код с помощью check50 перед собственным тестированием. Всё-таки check50 не в реальности не существует, поэтому запуск кода с вашими собственными входными выборками, сравнение фактического вывода к ожидаемым выходных данных, — лучшая привычка, которую можно обрести на этом этапе. Правда-правда, не приобретайте вредную зависимость!

Если вам интересно поиграть с реализацией find, выполненной ассистентами cs50, выполните такую команду: 

~cs50/pset3/find

Сортировка

Линейный поиск — это не то, что по-настоящему впечатляет, но с первых лекций (а в седьмой мы снова повторили этот трюк!) вы помните, что найти иглу в стоге сена можно гораздо быстрее. Только сперва нужно отсортировать наш стог сена.

Обратите внимание: find.c вызывает sort, функцию, объявленную в helpers.h. К сожалению, мы «забыли» реализовать эту функцию в полной мере в helpers.c! Загляните в helpers.c, и вы увидите, что происходит мгновенное возвращение sort, хотя функция main из find действительно передает ей фактический массив.

Теперь вспомните синтаксис объявления массива. Вы не только указываете тип элементов этого массива, но также задаете его размер в квадратных скобках. Так мы делаем для haystack в find.c:

int haystack [MAX];

Но при прохождении массива вы только указываете его имя. И мы это делаем точно так же, когда проходим haystack, чтобы выполнить sort в find.c:

sort (haystack, size);

(Догадайтесь, почему мы передаем размер этого массива отдельно?)

Когда мы объявляем функцию, которая принимает в качестве аргумента одномерный массив, не нужно указывать размер массива. Точно так же мы этого не делали, когда объявляли sort в helpers.hhelpers.c):

void sort (int values [] int n);

Реализуйте sort, так чтобы функция действительно отсортировывала массив чисел от меньших к большим. Нужно, чтобы время ее работы было равно O (n2), где n — размер массива. Скорее всего, вы захотите реализовать метод пузырьковой сортировки, сортировка выбором, или вставкой, поскольку мы изучили их на третьей неделе. Тем не менее, важно отметить: «правильного» способа реализации не существует для этих алгоритмов. Есть огромное количество вариаций. На самом деле, вы всегда можете улучшить их, если считаете нужным, до тех пор, пока ваша реализация сойдется к O (n2). Тем не менее, эксперименты оставьте для тела функции, а с целью упрощения проверки, не меняйте нашу декларацию sort. Она должна выглядеть так:

void sort (int values [] int n);

Поскольку функция возвращает значение типа void, она не должна возвращать отсортированный массив. Вместо этого, она должна «деструктивно» сортировать фактический массив, по которому она «пробегает», перемещая его элементы. На четвертой неделе мы будем обсуждать, что массивы передаются не по значению, а по ссылке. То есть, sort не получит копии массива, а сам исходный массив.

Хотя вы не можете изменить нашу декларацию sort, вы всегда можете определить свою собственную функцию в helpers.c, которые затем можно будет вызвать из sort.

Выберите сами, как лучше протестировать реализацию вашей sort. Не стоит забывать, что printf и GDB вам помогут! И не забывайте, что вы можете создать ту же последовательность псевдослучайных чисел снова и снова, явно указывая значения семян для generate.

Бинарный поиск, советы

Первое, что необходимо понимать о функции find — у нас уже есть написанный код (дистрибутив). Мы просто заполняем некие пробелы в уже существующем коде. 

Программа find.c запрашивает ввод чисел (то есть, заполнить «стог»), а потом ищет в нем «иголку» по запросу пользователя, используя для этого sort и search — функции, определенные в helpers.c. Таким образом, find уже написана, нам нужно написать helpers

Итак, вот что нам нужно сделать:

  • Реализовать search. Эта функция должна возвращать true, если значение найдено в «стогу» и false, если его там нет.
  • Реализовать sort, функцию, сортирующую массив значений.

Изначально поиск реализован как линейный. Но вы можете сделать лучше. Алгоритм линейного поиска выполняется за время О(n). Ваша задача — написать алгоритм бинарного поиска. Время его исполнения — O(log n). Однако его проблема в том, что ему на вход нужно подавать отсортированные данные, иначе он не будет работать. Вы помните пример с разорванной книгой, и, вероятно, знаете, почему так.

Если вы прошлись бинарным поиском по элементам, и их больше не осталось (то есть в этом «стоге» нашей «иголки» попросту нет), нужно, чтобы функция вернула false

Бинарный поиск можно реализовать итеративно или рекурсивно (о рекурсии Дэвид Малан рассказал в восьмой лекции).

Комментарии (19)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Shahin Уровень 2
19 ноября 2022

/**
 * helpers.c
 *
 * Computer Science 50
 * Problem Set 3
 *
 * Helper functions for Problem Set 3.
 */

#include <cs50.h>

#include "helpers.h"

/**
 * Returns true if value is in array of n values, else false.
 */
bool search(int value, int values[], int n) // binary search for a specific value
{
    int left = 0, right = n-1;

    while(left<=right)
    {
        int middle = left + (right - left)/2;

        if (values[middle] == value) return true;

        else if ( values [middle] > value)  right = middle - 1;

        else   left = middle + 1;

     }
    // TODO: implement a searching algorithm
    return false;
}

/**
 * Sorts array of n values.
 */
void sort(int values[], int n)  // selection sort by increment(по возрастанию)
{

    for (int i = 0; i<n; i++)
        {
            int min = i;

            for(int j = i + 1; j < n; j++)
                if(values[j] < values[min])
                             min = j;

            int temp = values[i];
            values[i] = values[min];
            values[min] = temp;
        }
    // TODO: implement an O(n^2) sorting algorithm
    return;
}
Dannelion Уровень 1
29 октября 2020
HELPERS.C

/**
 Реализация бинарного поиска через рекурсию.
 */
#include <cs50.h>
#include <stdio.h>

#include "helpers.h"

int resultSearch;

void search(int values[], int n, int start, int end)
{
    // TODO: implement a searching algorithm
    int m = (start + end) / 2;

    if (values[m] == n)
        resultSearch = 1;
    else if (end - start > 0)
    {
        if (n < values[m])
            search(values, n, start, m-1);
        else
            search(values, n, m+1, end);
    }
    else
        resultSearch = 0;
}

/**
 * Sorts array of n values.
 */
void sort(int values[], int n)
{
    bool sorted = true;
    int t;
    do
    {
        sorted = true;
        for (int i = 0; i < n - 1; i++)
        {
            if (values[i] > values[i+1])
            {
                t = values[i];
                values[i] = values[i+1];
                values[i+1] = t;
                sorted = false;
            }
        }
    }
    while (!sorted);
}
FIND.C

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>

#include "helpers.h"

extern int resultSearch;

const int MAX = 65536;

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./find needle\n");
        return -1;
    }

    int needle = atoi(argv[1]);

    int size;
    int haystack[MAX];
    for (size = 0; size < MAX; size++)
    {
        int straw = get_int("");
        if (straw == INT_MAX)
            break;

        haystack[size] = straw;
        printf("\nhaystack[%i] = %i", size, straw);
    }
    printf("\n");

    sort(haystack, size);
    search(haystack, needle, 0, size-1);
    
    if (resultSearch == 1)
        printf("\nFound needle %i in haystack!\n\n", needle);
    else
        printf("\nDidn't find needle %i in haystack.\n\n", needle);
}
Ихтияр Кадыров Уровень 1
25 февраля 2020

/**
 * Returns true if value is in array of n values, else false.
 */
bool search(int value, int values[], int n)
{
   int left = 0;
   int right = n-1;
   int middle;

   if (value < 1)
   {
      return false;
   }

   while (left <= right)
   {
   		middle = (left + right) / 2;

   		if (value == values[middle])
         {
            return middle;
         }
         else if (value < values[middle])
         {
            right = middle - 1;
         }
         else if (value > values[middle])
         {
            left = middle + 1;
         }
   }

   return false;
}

/**
 * sort сортирует массив
 * 
 * @param values [массив в котором производится сортировка]
 * @param n      [количество элементов в массиве]
 */
void sort(int values[], int n)
{
   int left = 0; 
   int right = n;
   int minElement = -1, 
	    maxElement = -1;

   while (left < right) 
   {
      findMinMax(values, left, right, &minElement, &maxElement);

		if (minElement != left) 
		{
			if (maxElement == left)
         {
			   maxElement = minElement;
         }

			swap(&values[left], &values[minElement]);
		}

		if (maxElement != right-1) 
		{
			swap(&values[right-1], &values[maxElement]);
		}

		left++;
		right--;
   }

    return;
}
Ихтияр Кадыров Уровень 1
25 февраля 2020

/**
 * findMinMax ищет минимальный и максимальный элемент
 * 
 * @param values     [массив в котором производится поиск]
 * @param left       [граница массива с которого начинается поиск]
 * @param right      [граница массива на котором заканчивается поиск]
 * @param minElement [указатель на минимальный элемент]
 * @param maxElement [указатель на максимальный элемент]
 */
void findMinMax(int values[], int left, int right, int* minElement, int* maxElement)
{
	*minElement = left;
	*maxElement = left;

	while(++left < right)
   {
      if (values[left] < values[*minElement])
      {
         *minElement = left;
      } 
      else if (values[left] > values[*maxElement])
      {
         *maxElement = left;
      }
   }
}

/**
 * swap производит обмен между двумя
 *      целочисленными переменными
 * 
 * @param left  [указатель на левую переменную]
 * @param right [указатель на правую переменную]
 */
void swap(int* left, int *right) 
{

   if (*left == *right)
   {
      return;
   }

   register int tmp = *left;
            *left = *right;
            *right = tmp;

}
Александр Уровень 1
29 сентября 2020
по строке 51: findMinMax(values, left, right, &minElement, &maxElement); ошибка: implicit declaration of function 'findMinMax' is invalid in C99 [-Werror,-Wimplicit-function-declaration] может кто-то подскажет в чем причина? может библиотеки не хватает или в самой функции ошибка? Спасибо
Ихтияр Кадыров Уровень 1
12 февраля 2020
/* Комментарий удален */
Даниил Уровень 41 Master
10 февраля 2020
Исправте ошибку, не хватает запятой во всех местах где используеться стока

void sort (int values [] int n);
Это может кого-то нехило так запутать...
Александр Уровень 1
29 сентября 2020
а еще ; здесь на конце не нужна
Max Уровень 41
16 сентября 2018
Всем привет. Все три О(n2) сортировки проверил - работает. Сортировку слиянием - не осилил (точнее не уделял особо времени, а с наскока не въехал, как делать) Бинарный поиск: корректно получилось реализовать только итерационный вариант, рекурсивный работает неправильно у меня. Если у кого есть сортировка слиянием и бинарный поиск через рекурсию именно для этого задания, киньте в коменты или в личку, пожалуйста. А да, совет таким же, как и я новичкам - создавайте отдельные независимые файлики, хардкордте массив для теста - так намного проще понять принцип и отловить ошибки. Ну это я по себе сужу)))
Sergey Petrov Уровень 0
30 сентября 2018
"Сортировку слиянием - не осилил" +1, потом посмотрел реализацию и приуныл, а потом обнаружил что по заданию оно и не надо)
Schamanenok Уровень 3
16 августа 2018
Помогите пожалуйста не понимаю ,что нужно сделать
Кирилл Уровень 29
5 сентября 2018
Ответил выше
Soso Sukhitashvili Уровень 0
2 августа 2018
У меня есть только одна ошибка -> :( finds 28 in {28,29,30} expected exit code 0, not 1 <- кто-нибудь знает, как исправить код?
Александр Уровень 0
15 октября 2017
Выдаёт какую-то странную ошибку при попытке скомпилировать helpers.c - вроде проблема не в коде, а в ide.CS50, кто-нибудь сталкивался с подобным? как решить?

~/workspace/pset3/find/ $ make helpers
clang -fsanitize=signed-integer-overflow -fsanitize=undefined -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wshadow    helpers.c  -lcrypt -lcs50 -lm -o helpers
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
...дальше аналогичный текст ещё на несколько десятков строк, а потом:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [helpers] Error 1
enot_00 Уровень 1
28 октября 2018
У меня было то же самое, но при этом функции из helpers.c нормально работают. Это какой-то нюанс компиляции.
Ярослав Уровень 0
30 августа 2020
Это ж вроде как один из файлов библиотеки (*.h и *.c) и их не надо компилировать, они сами компилируются когда подтягиваются в основную программу. Вроде так.
Pasha Zyx Уровень 1
26 августа 2017
А как коммитить (отправлять) проекты на github?
Roman Andreev Уровень 1
14 октября 2017
Итак, во первых, рекомендую ознакомиться с актуальным задачником (на момент написания 2017) по ссыле И обратить внимание на то, что даже в less версии задания требуется реализовать бинарный поиск, реализация которого должна выполнятся за O(log n) Во вторых: обновить нашу виртуальную среду разработки cs50.io командой

update50
Далее, после выполнения задания ( описываю вариацию less, c more всё тоже самое по аналогии) выполняем команду submit50:

submit50 cs50/2017/x/find/less
при первом вводе запросит логин и пароль от гитхаба. И после этого мы наконец таки сможем проверить правильность написания нашего кода командой check50:

check50 cs50/2017/x/find/less
Ярослав Уровень 0
30 августа 2020
" даже в less версии задания требуется реализовать бинарный поиск, реализация которого должна выполнятся за O(log n)" LESS comfortable - это и есть сложнее вариант. так что понятно что там сложнее задание, так как надо сделать сортировку сначала, а потом бинарный поиск, который без сортировки не пашет. Спасибо за инфу об авторизации.