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

Задание 1: Search

Открыта

Пришло время для кое-чего интересного! Обратите внимание, 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.h (и helpers.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, 15:25
/**
 * 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, 18:35
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);
}
25 февраля 2020, 00:52
/**
 * 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;
}
25 февраля 2020, 09:04
/**
 * 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, 18:33
по строке 51: findMinMax(values, left, right, &minElement, &maxElement); ошибка: implicit declaration of function 'findMinMax' is invalid in C99 [-Werror,-Wimplicit-function-declaration] может кто-то подскажет в чем причина? может библиотеки не хватает или в самой функции ошибка? Спасибо
12 февраля 2020, 18:33
/* Комментарий удален */
ДаниилSalesforce Developer в CustomertimesMaster
10 февраля 2020, 14:10
Исправте ошибку, не хватает запятой во всех местах где используеться стока
void sort (int values [] int n);
Это может кого-то нехило так запутать...
Александр
Уровень 1
29 сентября 2020, 18:15
а еще ; здесь на конце не нужна
Max
Уровень 41
16 сентября 2018, 21:04
Всем привет. Все три О(n2) сортировки проверил - работает. Сортировку слиянием - не осилил (точнее не уделял особо времени, а с наскока не въехал, как делать) Бинарный поиск: корректно получилось реализовать только итерационный вариант, рекурсивный работает неправильно у меня. Если у кого есть сортировка слиянием и бинарный поиск через рекурсию именно для этого задания, киньте в коменты или в личку, пожалуйста. А да, совет таким же, как и я новичкам - создавайте отдельные независимые файлики, хардкордте массив для теста - так намного проще понять принцип и отловить ошибки. Ну это я по себе сужу)))
Sergey Petrov
Уровень 0
30 сентября 2018, 20:54
"Сортировку слиянием - не осилил" +1, потом посмотрел реализацию и приуныл, а потом обнаружил что по заданию оно и не надо)
Schamanenok
Уровень 3
16 августа 2018, 14:17
Помогите пожалуйста не понимаю ,что нужно сделать
Кирилл
Уровень 29
5 сентября 2018, 15:40
Ответил выше
Soso Sukhitashvili
Уровень 0
2 августа 2018, 19:23
У меня есть только одна ошибка -> :( finds 28 in {28,29,30} expected exit code 0, not 1 <- кто-нибудь знает, как исправить код?
Александр
Уровень 0
15 октября 2017, 12:41
Выдаёт какую-то странную ошибку при попытке скомпилировать 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, 08:42
У меня было то же самое, но при этом функции из helpers.c нормально работают. Это какой-то нюанс компиляции.
ЯрославЧародей в Земля
30 августа 2020, 13:46
Это ж вроде как один из файлов библиотеки (*.h и *.c) и их не надо компилировать, они сами компилируются когда подтягиваются в основную программу. Вроде так.
Pasha Zyx
Уровень 1
26 августа 2017, 13:56
А как коммитить (отправлять) проекты на github?
Roman Andreev
Уровень 1
14 октября 2017, 11:29
Итак, во первых, рекомендую ознакомиться с актуальным задачником (на момент написания 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
ЯрославЧародей в Земля
30 августа 2020, 14:42
" даже в less версии задания требуется реализовать бинарный поиск, реализация которого должна выполнятся за O(log n)" LESS comfortable - это и есть сложнее вариант. так что понятно что там сложнее задание, так как надо сделать сортировку сначала, а потом бинарный поиск, который без сортировки не пашет. Спасибо за инфу об авторизации.